Agent sign-up
Let an AI agent get a Narwhal API key for its user.
An AI agent can get a Narwhal API key for its user without a browser. The agent sends the user's email address and gets a working key at once, with a small trial credit. The user reads a 6-digit code from an email to the agent, the agent verifies it, and the same key becomes a full account.
Agents can read the same flow as plain markdown at /.
Rules for agents
- Get the user's OK before you sign them up.
- Use only an email address the user gave you.
- Never read the code from the user's mailbox yourself. The user must give it to you.
- Retry
429and503at most 3 times, waiting the seconds inRetry-After. Then stop and tell the user. IfRetry-Afteris over 300 seconds, a daily limit was reached: stop at once and tell the user. - After
403or410, ask the user before starting a new sign-up. Each one emails them another code.
Request a key
Send the user's email address. No API key is needed. The answer is always status 202 with the same text, so it cannot be used to find out whether an address already has an account. The api_ works at once on a pending account.
curl --request POST \
--url https://api.narwhalapi.com/v1/agent-signup \
--header "Content-Type: application/json" \
--data '{"email": "person@example.com", "agent_name": "Claude Code", "client": "claude-code"}'{
"status": "pending",
"message": "If this email address can be used, Narwhal emailed it a 6-digit code. Ask the user to read the code to you, then call POST /v1/agent-signup/verify with it. The api_key works now with a small trial credit.",
"api_key": "nw_live_...",
"trial_credit_micro": 200000,
"expires_at": "2026-11-16T09:00:00Z",
"verify_path": "/v1/agent-signup/verify"
}Request fields
| Field | Required | Meaning |
|---|---|---|
email | Yes | The user's email address. Narwhal emails a 6-digit code to it. Disposable-mail domains are refused. |
agent_ | Yes | What the agent calls itself: 1 to 80 letters, digits, spaces and _. A dot is allowed only between two digits, as in a version number. No URLs, domains or colons. The email shows it to the user. |
client | No | The tool the agent runs in, such as claude-code or cursor: lowercase letters, digits, _ and -, at most 40 characters. |
Response fields
| Field | Meaning |
|---|---|
status | Always pending until the code is verified. |
message | The same text for every request. |
api_ | A working key on a pending account. Keep it secret; it is shown once. |
trial_ | The one-off trial credit in micro-dollars (1,000,000 is $1). 200000 is $0.20. |
expires_ | When the key stops working if it is not verified: 7 days after the request. |
verify_ | Where to send the code the user reads back. |
If the address already has an account, its owner gets an email that says to sign in, and no code. A new address gets the code email. The code lasts 15 minutes and allows 5 wrong attempts. An address gets at most 3 code emails in 30 days.
Verify the code
The agent asks the user to read the 6-digit code aloud, then sends it with the key from the first step as a Bearer token. Sharing the code is how the user accepts the Terms. Verify is free and works at zero credit.
curl --request POST \
--url https://api.narwhalapi.com/v1/agent-signup/verify \
--header "Authorization: Bearer $NARWHAL_API_KEY" \
--header "Content-Type: application/json" \
--data '{"code": "123456"}'{
"status": "claimed",
"message": "Verified. This key is now a normal Narwhal account with the free monthly credit. When you show Narwhal data to the user, credit it as \"Data by Narwhal API\"."
}Request fields
| Field | Required | Meaning |
|---|---|---|
code | Yes | The 6-digit code from the email Narwhal sent to the user. |
Response fields
| Field | Meaning |
|---|---|
status | claimed once the code is accepted. |
message | Confirms the account and repeats the attribution rule. |
On success the same key becomes a normal account, and the free credit of $10 a month for 12 months starts on the verify date.
Check the status
Free, and it needs the key. It says where the sign-up stands.
curl --request GET \
--url https://api.narwhalapi.com/v1/agent-signup/status \
--header "Authorization: Bearer $NARWHAL_API_KEY"{
"state": "pending",
"trial_credit_left_micro": 200000,
"expires_at": "2026-11-16T09:00:00Z",
"code_expires_at": "2026-11-09T09:15:00Z",
"attempts_left": 5
}Response fields
| Field | Meaning |
|---|---|
state | pending, claimed, expired or blocked. |
trial_ | Trial credit left, in micro-dollars. Present while pending. |
expires_ | When the key stops working if it is not verified. Present while pending. |
code_ | When the emailed code stops working. Present while pending. |
attempts_ | Wrong codes left before the code is locked. Present while pending. |
verified_ | When the user verified. Present once claimed. |
Limits before and after verify
| Before verify (pending) | After verify | |
|---|---|---|
| Credit | A one-off trial of $0.20, about 100 data calls at $2 per 1,000. | $10 a month free for 12 months, no card, then prepaid top-ups. |
| Requests a minute | 10 | 50 on free credit, 500 once the account holds paid credit. |
| Webhooks, event streams, team invites and top-ups | Not available. | Available. |
| Lifetime | 7 days, then the key is revoked. | Normal rules. |
Every response to a pending key carries X-Narwhal-Account-State: pending. Sign-up, verify and status calls are never charged. Prices are $2 per 1,000 calls, and $10 per 1,000 for Companies.
When the trial credit runs out, a data call returns 402 insufficient_ with verify_ and a message_ for the agent to show.
{
"status": 402,
"code": "insufficient_credits",
"verify_required": true,
"message_for_user": "The trial credit on this Narwhal key is used up. To go on, please read me the 6-digit code Narwhal emailed you. When Narwhal data is shown to you, it is credited \"Data by Narwhal API\"."
}If the user never shares the code, the key keeps working until the trial credit runs out or 7 days pass.
The code email names the agent, its tool and the country of the request. It has a link called "This wasn't me" that opens a page with one button. Opening the link changes nothing; pressing the button revokes the key and stops Narwhal emailing that address.
Errors
Errors use the problem shape in the errors guide: branch on status, then code.
| Status | Public code | Endpoint | Meaning |
|---|---|---|---|
400 | invalid_ | All | A field is missing or malformed, for example an email that is not a plain address or a code that is not 6 digits. |
400 | invalid_ | Verify | The code is wrong. The message says how many attempts are left. |
401 | invalid_ | Verify, status | The Bearer key is missing, malformed, unknown or revoked. |
402 | insufficient_ | Data calls | The trial credit is used up. The body adds verify_ and message_. |
403 | code_ | Verify | Too many wrong codes. Ask the user before starting a new sign-up. |
403 | account_ | Webhooks, stream tickets | A pending key cannot use webhooks or event streams. |
404 | not_ | Status | This key did not come from agent sign-up. |
404 | block_ | Block | The link in the email is not valid. |
404 | route_ | All | Agent sign-up is not switched on. |
409 | email_ | Verify | The address already has an account. The key is revoked; ask the user to sign in instead. |
409 | already_ | Verify, block | The key is already verified. A code cannot be used twice. |
409 | not_ | Verify | The sign-up is no longer waiting to be verified (expired or blocked). |
409 | verify_ | Verify, block | Another verification of this key is running. Check the status. |
410 | code_ | Verify | The code has expired. Ask the user before starting a new sign-up. |
422 | agent_ | Request | The agent name has characters outside the allowed set. |
422 | email_ | Request | The email domain is a disposable-mail provider. |
429 | rate_ | All | Too many requests from this network, or too many a minute for a pending key. Wait the number of seconds in Retry-After. |
429 | domain_ | Verify | Too many accounts were verified today for this email domain. Try the same code again after 00:00 UTC. |
503 | agent_ | Request, block | Sign-up is temporarily unavailable or at its daily limit. See Retry-After. |
503 | verify_ | Verify | Verification is temporarily unavailable. Try the same code again. |
Data by Narwhal API
While an account holds only free credit, show Narwhal data to people with the credit "Data by Narwhal API" and a link to Narwhal API. The verify message and the message_ of a 402 repeat this rule. Accounts with paid credit may show data without a credit.
Sign in later
After verify, the user can sign in with Google or GitHub using the same email address. That opens the same account, with the same key and balance. The authentication guide covers storing and rotating the key.
Was this page helpful?