Authentication
ReplyNodes has two first-class authentication options:
- MCP OAuth — interactive MCP clients use Better Auth at
https://auth.replynodes.comwith themcp:readscope. - API key — headless clients, CI, SDKs, and manual MCP configurations use
an
rn_test_*orrn_live_*key.
OAuth access is resolved to the authenticated ReplyNodes organization. Clients
must not send an organization id, and API keys remain supported alongside
OAuth. Sign in or manage access at
https://platform.replynodes.com/sign-in and manage access at https://platform.replynodes.com/api-keys.
The canonical OpenAPI document defines HTTP Bearer authentication for the public API. Send the raw key after the Bearer scheme in the Authorization header. Key formats documented by the contract are rn_test_<64hex> and rn_live_<64hex>; never place a real key in a URL, browser bundle, prompt, log, or repository.
The gateway CLI reads FETCHER_API_KEY and constructs this header for requests. Other clients should use their secret-store equivalent and follow the same HTTP header contract.
See Quickstart for a request that fails closed when the environment variable is absent.
Agents
Start keyless: the capabilities listed in the Agent Quickstart
work without a key up to their daily limits. Get a key only when a call
returns 429 anonymous_limit_reached or 401; that response's
error.next_action names the exact call (method, url,
required_fields), a human_message for the human, and
retry_after_seconds.
Email sign-up (recommended)
No browser is needed. The human only reads one 6-digit code from their inbox.
-
Ask the human which email to use, then sign up. The response is immediate:
curl -sS -X POST https://platform.replynodes.com/api/agent/signup \ -H 'Content-Type: application/json' \ -d '{"email":"you@example.com","agent_name":"my-agent"}'201(or200when the same email signs up again) returnsapi_key(a restricted key, shown once),status: "restricted",expires_at(72 hours),verification(verify and resend endpoints,code_sent),human_message, andstatus_url. Showhuman_messageto the human. Storeapi_keyonce asREPLYNODES_API_KEY; never print or log it. A restricted key already calls the keyless capabilities on its own daily allowance and never spends credits. -
When the human pastes the code, verify with the same key:
curl -sS -X POST https://platform.replynodes.com/api/agent/verify \ -H "Authorization: Bearer ${REPLYNODES_API_KEY}" \ -H 'Content-Type: application/json' -d '{"code":"123456"}'200returnsstatus: "verified", the newexpires_at(30 days), andworkspace(createdorexisting: an email that already has an account attaches to that workspace). The key string does not change. Repeating verify is safe, including after a timeout. -
GET https://api.replynodes.com/v1/agent/statuswith the key returns its status, per-key quota (restricted) or credit balance, expiry, andnext_actionswith ahuman_message(verify, renew, add credits).
Codes are 6 digits, valid 10 minutes, 5 attempts; the newest code replaces
older ones. Resend with POST https://platform.replynodes.com/api/agent/verify/resend
and the same Bearer key (at most one code per minute and five per day per
email). Renew a key before it expires by signing up again with the same email
and verifying the new code.
When the workspace runs out of credits, credit-metered calls return HTTP 402 with
code: "topup_required" and next_action.action: "top_up". Show its
human_message: only the human can add credits, on the billing page in
next_action.url. Retry once they confirm; do not retry in a loop.
| HTTP | error | Agent action |
|---|---|---|
| 400 | email_not_accepted | Ask for the human's real, non-disposable email. |
| 400 | invalid_code | Ask the human to re-read the newest email (attempts_remaining). |
| 400 | code_expired, code_attempts_exhausted | Resend (next_action), then ask for the newest code. |
| 401 | invalid_api_key | The key is superseded or expired; sign up again with the same email. |
| 403 | agent_key_not_permitted | Show human_message: a workspace admin must allow agent keys. |
| 409 | account_sign_in_required | Show human_message: the human signs in once on the web with an email code. |
| 410 | onboarding_expired | Sign up again. |
| 429 | rate_limited, resend_limited | Wait Retry-After. |
| 503 | email_signup_unavailable | Use the device flow in next_action. |
| 503 | temporarily_unavailable | Repeat the same request after Retry-After. |
On the API, a restricted key outside the keyless capabilities gets
403 verification_required, and a used-up per-key allowance gets
429 restricted_limit_reached; both carry next_action: verify_email.
Legacy device flow
Agents can still obtain a read-only API key through a short claim approved once by a human in a browser. Start a claim with:
curl -sS -X POST https://platform.replynodes.com/api/agent/claim \
-H 'Content-Type: application/json' \
-d '{"agent_name":"my-agent","agent_platform":"my-platform"}'agent_name and agent_platform are optional labels. A successful response
contains claim_id, device_code, user_code, verification_uri,
verification_uri_complete, expires_in (900 seconds, or 15 minutes),
interval (3 seconds), and docs. Give the human the exact
verification_uri_complete link: it already includes the user_code. Wait
for that one approval, then poll at the returned interval:
curl -sS -X POST https://platform.replynodes.com/api/agent/claim/token \
-H 'Content-Type: application/json' \
-d "{\"claim_id\":\"${CLAIM_ID}\",\"device_code\":\"${DEVICE_CODE}\"}"On 200, store api_key once as REPLYNODES_API_KEY; never log it or ask
the token endpoint for it again. The returned expires_at is authoritative.
The default issued agent_read key lifetime is 30 days. If a later API call
returns 401, start a new claim. The historical scope name is
read:x_public; it means public provider reads and is not a request to rename
the scope.
An agent key reaches only read-only provider GET and HEAD routes,
/v1/capabilities, and the provider /v1/x surface where that surface is
covered by provider-read routing. It cannot use non-GET methods or key,
dashboard, internal, billing, wallet, checkout, webhooks, usage, overview, or
me routes. Check /v1/capabilities for the current provider contract; this
page does not promise PDF conversion or another route outside that allowlist.
Token polling outcomes
In this table, —/omitted status means the response body does not include the status field. The status is omitted on paths where the code does not return it, including some 503 temporarily_unavailable responses.
| HTTP | error | status | Retry-After | Agent action |
|---|---|---|---|---|
| 200 | — | approved / state: revealed | — | Store api_key once with its expires_at; stop polling. |
| 428 | authorization_pending | pending | — | Wait interval seconds, then poll again. |
| 429 | poll_rate_limited | — | Returned header | Wait for Retry-After; do not loop faster. |
| 409 | already_delivered | delivered | — | Stop; the key was already revealed. Use the dashboard recovery details. |
| 409 | already_delivered | delivery_uncertain | — | Do not start a new claim blindly; check with the human / Agent keys page; then start a new claim if the key was not stored. |
| 410 | expired_token | expired when present | — | Stop and start a new claim. |
| 403 | authorization_failed | authorization_failed when present | — | Stop; after human approval, restart the claim. |
| 404 | invalid_claim | — | — | The claim id or device code is invalid; stop and start a new claim. |
| 503 | temporarily_unavailable | delivery_uncertain when returned; omitted on some paths | Returned header when present | Wait and retry while claim recovery remains possible. |
| 503 | delivery_uncertain | — | — | Retry while claim recovery remains possible. |
| 409 | delivery_uncertain | delivery_uncertain | — | Retries are exhausted; stop and use dashboard recovery. |
For the complete protocol, use https://replynodes.com/auth.md.