Agent Mail by Ackuna

Every page here is also markdown — add .md to the URL.

Sign up

An agent can create its own Ackuna organization and API key. Two requests, one emailed code. No key is needed for either request.

API sign-up can be closed by the operator, independently of the console's registration form. While it is closed, both requests answer 503 registration_disabled and nothing is created or mailed; a code issued earlier cannot be redeemed until it reopens.

1. Ask for a code

POST /v1/signup
Content-Type: application/json

{"email": "ops@yourcompany.com", "organization_name": "Robot Co"}
{"data": {"object": "signup", "signup_id": "sgn_01k4c2v8qf3m7d5b0y6xz4rt1c", "email": "ops@yourcompany.com", "expires_at": "2026-09-04T12:15:00.000Z"}}

The status is 202: nothing has been created yet, a code has only been mailed to that address. It is valid for 15 minutes and 5 attempts. The address is normalized before use — lower-cased, a +tag in the local part is dropped, and for Gmail (gmail.com, googlemail.com) dots are dropped and the domain becomes gmail.com — so ops+robots@yourcompany.com and ops@yourcompany.com are the same account, and so are jane.doe@gmail.com and janedoe@googlemail.com. Disposable mail providers are refused with 422 invalid_request. If the address already belongs to an Ackuna user you get 409 email_already_registered: sign in to the console with it and issue a key there instead.

One code per address at a time. While a code is live, asking again answers 409 signup_pending and mails nothing; the first code keeps working, and Retry-After says when it expires. Keep the signup_id from the first response.

Rate limits on this path are deliberately tight: 5 requests per hour per IP, 3 per hour per address, and 5 completed sign-ups per IP per day shared with the console's registration form; the verify step allows 30 per hour per IP on top of the 5 attempts per code. Read the code from the mailbox — do not loop on this request.

2. Prove the mailbox

POST /v1/signup/verify
Content-Type: application/json

{"signup_id": "sgn_01k4c2v8qf3m7d5b0y6xz4rt1c", "code": "K7MP2XQ9"}
{"data": {
  "object": "signup_completed",
  "organization": {"id": "org_01k4c2v8qf3m7d5b0y6xz4rt2d", "name": "Robot Co"},
  "api_key": {"key": "ak_9f2c1e7b4a6d8035f1c9a2b7e4d6f803a1b2c3d4", "prefix": "ak_9f2c1e7b4", "scopes": ["inboxes:read", "inboxes:write", "messages:read", "messages:send", "webhooks:read", "webhooks:write", "domains:read", "domains:write"]},
  "console_url": "https://agents.ackuna.com/login?product=agent-mail",
  "docs_url": "https://agents.ackuna.com/docs/quickstart",
  "console_claim": "Use \"Forgot password\" on the console login page; the owner account has no password yet."
}}

The status is 201: the organization, its owner account and that key now exist. The key is shown once — store it now. console_claim is the sentence to hand a human who wants the console: the owner account was created without a password, so it is claimed through "Forgot password" rather than a normal sign-in. A wrong code answers 422 signup_code_invalid with the attempts remaining; an expired, used or exhausted sign-up answers 410 signup_expired — ask for a new code; and if a person registered that address through the web form while the code was in flight, verification answers 409 email_already_registered — sign in to the console and issue a key there instead.

What you have now

Continue with the Quickstart from step 2.