Browse all documentation

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 /auth.md.

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 429 and 503 at most 3 times, waiting the seconds in Retry-After. Then stop and tell the user. If Retry-After is over 300 seconds, a daily limit was reached: stop at once and tell the user.
  • After 403 or 410, 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_key works at once on a pending account.

POST /v1/agent-signup
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"}'
Response
{
  "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

FieldRequiredMeaning
emailYesThe user's email address. Narwhal emails a 6-digit code to it. Disposable-mail domains are refused.
agent_nameYesWhat 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.
clientNoThe tool the agent runs in, such as claude-code or cursor: lowercase letters, digits, _ and -, at most 40 characters.

Response fields

FieldMeaning
statusAlways pending until the code is verified.
messageThe same text for every request.
api_keyA working key on a pending account. Keep it secret; it is shown once.
trial_credit_microThe one-off trial credit in micro-dollars (1,000,000 is $1). 200000 is $0.20.
expires_atWhen the key stops working if it is not verified: 7 days after the request.
verify_pathWhere 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.

POST /v1/agent-signup/verify
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"}'
Response
{
  "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

FieldRequiredMeaning
codeYesThe 6-digit code from the email Narwhal sent to the user.

Response fields

FieldMeaning
statusclaimed once the code is accepted.
messageConfirms 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.

GET /v1/agent-signup/status
curl --request GET \
  --url https://api.narwhalapi.com/v1/agent-signup/status \
  --header "Authorization: Bearer $NARWHAL_API_KEY"
Response
{
  "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

FieldMeaning
statepending, claimed, expired or blocked.
trial_credit_left_microTrial credit left, in micro-dollars. Present while pending.
expires_atWhen the key stops working if it is not verified. Present while pending.
code_expires_atWhen the emailed code stops working. Present while pending.
attempts_leftWrong codes left before the code is locked. Present while pending.
verified_atWhen the user verified. Present once claimed.

Limits before and after verify

Before verify (pending)After verify
CreditA 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 minute1050 on free credit, 500 once the account holds paid credit.
Webhooks, event streams, team invites and top-upsNot available.Available.
Lifetime7 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_credits with verify_required: true and a message_for_user for the agent to show.

Response
{
  "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.

StatusPublic codeEndpointMeaning
400invalid_queryAllA field is missing or malformed, for example an email that is not a plain address or a code that is not 6 digits.
400invalid_codeVerifyThe code is wrong. The message says how many attempts are left.
401invalid_api_keyVerify, statusThe Bearer key is missing, malformed, unknown or revoked.
402insufficient_creditsData callsThe trial credit is used up. The body adds verify_required and message_for_user.
403code_lockedVerifyToo many wrong codes. Ask the user before starting a new sign-up.
403account_pendingWebhooks, stream ticketsA pending key cannot use webhooks or event streams.
404not_an_agent_accountStatusThis key did not come from agent sign-up.
404block_link_invalidBlockThe link in the email is not valid.
404route_not_foundAllAgent sign-up is not switched on.
409email_has_accountVerifyThe address already has an account. The key is revoked; ask the user to sign in instead.
409already_verifiedVerify, blockThe key is already verified. A code cannot be used twice.
409not_pendingVerifyThe sign-up is no longer waiting to be verified (expired or blocked).
409verify_in_progressVerify, blockAnother verification of this key is running. Check the status.
410code_expiredVerifyThe code has expired. Ask the user before starting a new sign-up.
422agent_name_not_allowedRequestThe agent name has characters outside the allowed set.
422email_domain_not_allowedRequestThe email domain is a disposable-mail provider.
429rate_limit_exceededAllToo many requests from this network, or too many a minute for a pending key. Wait the number of seconds in Retry-After.
429domain_daily_limitVerifyToo many accounts were verified today for this email domain. Try the same code again after 00:00 UTC.
503agent_signup_unavailableRequest, blockSign-up is temporarily unavailable or at its daily limit. See Retry-After.
503verify_unavailableVerifyVerification is temporarily unavailable. Try the same code again.

Read the full errors guide

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_for_user 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?