AgentStack Docs

REST API: agent sign-up

Let an AI agent create its own sandbox workspace and API key, then have a human claim it.

POST /agent/sign-up is the only anonymous endpoint in the REST API. Every other endpoint needs an API key. It exists because an AI agent has no key yet, and no way to click through a dashboard to get one. POST /agent/verify sits beside it and does need the key, for the reason given below.

Sign up

POST /agent/sign-up takes one required field, human_email, and an optional org_name. Give the real email address of the person the agent works for. Do not invent an address. Placeholder domains and disposable inbox providers are rejected with BLOCKED_EMAIL_DOMAIN.

The response contains api_key. Store it immediately. The key is returned exactly once and is never retrievable again. There is no recovery: a lost key cannot be re-issued, because a door that re-issued one would hand anybody who knows the address a working key to a workspace they did not create. The key has write scope and expires after 7 days. The response also contains organization_id, claim_url, and the one-time credit balance.

At the same time, a 6-digit claim code goes to the named address. If claim_email_sent is false, the workspace and the key are still valid, but nobody received a code. The human can send themselves a new one from the claim page.

Every call creates a new workspace. Calling sign-up twice for the same address gives two separate workspaces, two keys and two claim emails, not one workspace with a rotated key. Sign up once, and store what you get.

What a sandbox workspace can do

A sandbox workspace runs on the sandbox plan. It reaches 8 of the API's route modules: agents, sources, ingest, chat, conversations, messages, activity, and analytics. Every other endpoint returns 403 SANDBOX_NOT_PERMITTED. Agents may only use models that cost 1 credit. A more expensive model returns 403 SANDBOX_MODEL_NOT_PERMITTED. Name no model on POST /agents and the agent gets openai/gpt-5.4-mini, which the plan permits. The workspace holds 25 credits. They never refresh.

The plan also sets the size of the work. One agent. Up to 25 crawled pages per website source, which also caps a larger maxPages. 200 KB of training content in total, shared by every source, not counted per source. A source that crosses that budget returns 403 LIMIT_EXCEEDED. Manual retrain is not available: POST /agents/{id}/ingest/retrain returns 403 PLAN_UPGRADE_REQUIRED, because manual retrain needs a Standard plan or higher. Indexing still runs by itself when a source finishes, so an agent does not need retrain to go live.

Put the agent on a website

The chat widget is not part of the REST API, and it is the one part of the work that survives a claim. Two calls prepare it.

POST /agents returns both id and publicId. Use id in every REST call. Use publicId in the embed snippet — the data-agent-id attribute takes the public identifier, not the agent id.

You do not have to set isPublic. POST /agents without that field creates a public agent: the endpoint leaves the field unset, so the column default of true applies. Do not spend a call on PATCH /agents/{id} with {"isPublic": true}, because it changes nothing. Read the field back with GET /agents/{id} if you want proof. Only a deliberate {"isPublic": false}, from the API or from the dashboard, closes an agent. An agent that an earlier release of the endpoint left unset is also not public. Reopen either one with PATCH /agents/{id} and {"isPublic": true}.

Read the two widget failures apart. No bubble at all means the eligibility check refused the widget before it drew: a wrong publicId, a closed agent, or a workspace already scheduled for deletion. All three answer with 204, and nothing renders. A bubble that opens but never answers is a different failure. It is almost always a spent balance: chat returns 402 INSUFFICIENT_CREDITS when creditsRemaining reaches zero. Send one message with your API key to read the real error, because the widget does not show it.

Visitor messages spend the same 25 credits that the agent's own test messages spend. Train and test the agent first. Add the snippet to the website last. See Embed the Chat Widget for the snippet and the domain rules.

Confirm the code

An unclaimed workspace is deleted 7 days after it is created. To keep it, the human must claim it in a browser.

POST /agent/verify takes organization_id and the 6-digit code, and it takes the API key from the sign-up response as a bearer token. A correct code confirms that the named human received the email and is real. It returns the claim URL and "claimed": false. Wrong codes, expired codes and unknown workspaces all return the same 400 INVALID_CLAIM_CODE. Five wrong codes lock the current code out; a correct code never costs an attempt. The human can send themselves a new code from the claim page.

The key is required because this endpoint spends those five attempts, and the claim link is only the organization id — it reaches the agent, the human's inbox, and anyone who saw either. Without a credential, five wrong guesses from a stranger killed the code sitting in the human's inbox. The workspace's own key is the identity the agent already has, so only that agent can spend its attempts. A missing or invalid key returns 401.

This endpoint does not take ownership, because ownership needs an owner: a user account that the workspace belongs to. It has no signed-in person, so it cannot create one. Holding the code does not confer ownership either — the agent may have been given it. The last step always happens in a browser, where a real person is signed in as the address the code was sent to.

What a claim changes

A claim moves the workspace from the sandbox plan to the free plan. The free plan does not include REST API access, so the API key returns 403 PLAN_UPGRADE_REQUIRED on every endpoint until the owner upgrades to a paid plan. The key itself stays valid and never expires. Until they claim, the workspace stays on the sandbox plan and the key keeps working.

A claim ends the key, not the agent. The chat widget carries no plan check, so an agent that is already public goes on answering visitors after the claim. Order the work accordingly: build, train, test, add the widget to the website, and ask for the claim last. Anything that needs the key must happen before that.

A claim adds no credits. The 25 sandbox credits transfer as they are, the free plan's 50 credits are granted only to a workspace a person creates, and the free plan has no monthly refill. Tell the human that hobby at $30 per month is what restores REST access if they want the agent to keep working on the workspace.

Claim in a browser

The claim_url in the sign-up response opens a page that shows the human what the agent built: the agent name, its system instruction, what it was trained on, and any action endpoints. The API key is never shown there. The human signs in with the address the agent named, enters the same 6-digit code, and either claims the workspace or deletes it. A delete removes the workspace and everything in it at once, and cannot be undone.

An account under any other address is refused with 403 CLAIM_ACCOUNT_MISMATCH, whether or not the named address has an AgentStack account. That is what keeps the agent out: it holds the link and it may hold the code, but it cannot hold a session on the human's mailbox.

The person who receives the claim email has their own guide: Claim a workspace an agent made. Agents can read the same flow as plain text at /agent.md.

Limits

Sign-up allows 5 calls per hour and 20 calls per day from one IP address, and 3 calls per day for one email address. Verification allows 10 calls per minute per IP address. A platform-wide daily cap also applies, and only requests that clear the per-caller limits count against it. Over any limit, the response is 429 with a Retry-After header. If the rate limiter itself is unavailable, both endpoints return 503 LIMITER_UNAVAILABLE and refuse the request. They fail closed on purpose.

On this page