# AgentStack for AI agents AgentStack builds AI support agents. You create an agent, train it on your own content, and then chat with it through a REST API. This page is for software. It tells an autonomous agent how to get a working API key without a human in a browser. Base URL: https://www.agentstack.build/api/v1 ## 1. Sign up Send one request. You need the email address of the person you work for. curl -sS -X POST https://www.agentstack.build/api/v1/agent/sign-up \ -H 'content-type: application/json' \ -d '{"human_email":"alex@acme.dev","org_name":"acme-bot"}' The response body is `{"success":true,"data":{...},"error":null}`. The `data` object contains: - `api_key` — your credential. It is returned one time only. Store it before you read the rest of the response. It cannot be retrieved again, and it cannot be re-issued. - `organization_id` — the workspace the key belongs to. You need it to verify the claim code. - `claim_url` — the page the human opens to take ownership. - `credits` — 25, granted once. - `default_model` — `openai/gpt-5.4-mini`. - `claim_email_sent` — `false` means the workspace and the key work, but the human received no code. They can send themselves one from the claim page. Use the key as a bearer token on every other call: curl -sS https://www.agentstack.build/api/v1/agents -H "Authorization: Bearer $AGENTSTACK_API_KEY" Give a real address. A 6-digit code goes to it, and that code is the only route to owning the workspace. Placeholder and disposable domains are refused with 400 `BLOCKED_EMAIL_DOMAIN`. Do not invent an address. Call sign-up once. Every call creates a NEW workspace with its own key and its own claim email — it is not a retry and not a resend. There is no way to recover a key you lost: an endpoint that handed one back would hand it to anyone who knows the address. ## 2. What you get A workspace on the `sandbox` plan: - 25 credits, granted once. They never refresh. One chat message costs 1 credit. - Models that cost 1 credit only. A more expensive model is refused with 403 `SANDBOX_MODEL_NOT_PERMITTED`. Name no model and you get `openai/gpt-5.4-mini`. - 1 agent, up to 25 crawled pages per website source. - 200 KB of training content. That is one budget shared by every source you add, not a limit per source. Over it, the next source is refused with 403 `LIMIT_EXCEEDED`. - 30 requests per minute. - These 8 route modules: agents, sources, ingest, chat, conversations, messages, activity, analytics. That is enough to build an agent, train it on websites, files and question and answer pairs, chat with it, put it live on a website, and read the conversations back. The next two sections are the whole job, end to end. ## 3. Build the agent Create it. Only `name` is required, and only `name` is a bad idea: an agent with no `systemInstruction` answers from its training content and nothing else — no role, no tone, no refusal rules. curl -sS -X POST https://www.agentstack.build/api/v1/agents \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" \ -H 'content-type: application/json' \ -d '{"name":"Acme Support","systemInstruction":"You answer questions about Acme. If the answer is not in your sources, say so."}' Keep `id` and `publicId` from the response. `id` addresses the agent in every call below. `publicId` is what puts it on a website — section 4. Train it. Three source types, three different shapes: **A website.** Returns as soon as the crawl is queued, not when it finishes. curl -sS -X POST https://www.agentstack.build/api/v1/agents//ingest/crawl-website \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" \ -H 'content-type: application/json' \ -d '{"url":"https://acme.dev/docs","maxPages":25}' Omit `maxPages` and you crawl 10 pages, not the whole site. Ask for more than 25 and it is capped, quietly. **A question and answer pair.** Ready the moment it returns. curl -sS -X POST https://www.agentstack.build/api/v1/agents//sources/qnas \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" \ -H 'content-type: application/json' \ -d '{"question":"Do you ship to Canada?","answer":"Yes, in 3 to 5 days."}' **A file.** Three steps, and the middle one is not this API. Ask for a URL, PUT the bytes to that URL yourself, then tell us to process it. curl -sS -X POST https://www.agentstack.build/api/v1/agents//sources/files/upload-url \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" \ -H 'content-type: application/json' \ -d '{"fileName":"handbook.pdf","fileType":"pdf","fileSize":184320}' curl -sS -X PUT "" --data-binary @handbook.pdf curl -sS -X POST https://www.agentstack.build/api/v1/agents//sources/files//process \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" Types allowed: pdf, docx, doc, txt, pptx, xlsx, xls, png, jpg, jpeg. 50 MB each. **Training is asynchronous and nothing calls you back.** There is no webhook. Poll the source lists and read the `status` field on each row: curl -sS https://www.agentstack.build/api/v1/agents//sources/websites \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" A website goes `pending` → `crawling` → `completed`, or `failed` with an `errorMessage`. A file goes `processing` → `extracting` → `embedding` → `completed`. Question and answer pairs need no polling. A crawl of an unreachable URL still answers 200 at the start — you learn it failed by polling. Indexing runs by itself once a source finishes. Do not look for a retrain endpoint: `POST /agents//ingest/retrain` answers 403 `PLAN_UPGRADE_REQUIRED` on this plan. Test it. `sessionId` is yours to keep — omit it and every message starts a new conversation with no memory of the last one. curl -sS -X POST https://www.agentstack.build/api/v1/chat \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" \ -H 'content-type: application/json' \ -d '{"agentId":"","message":"Do you ship to Canada?"}' The response carries `sessionId`, the reply, `creditsUsed` and `creditsRemaining`. **Watch `creditsRemaining`.** It is the only place your balance appears — no endpoint reports it, and nothing warns you before the last credit goes. At zero, chat answers 402 `INSUFFICIENT_CREDITS` and stays there. ## 4. Put it on a website This is the part that makes the work real, and it needs one field you would not guess: `publicId`, not `id`. There is nothing to switch on first. `isPublic` defaults to `true`, so the agent you created in section 2 is already open to visitors. Do not spend a call setting it. If you want to be sure, read it back: curl -sS https://www.agentstack.build/api/v1/agents/ \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" That response carries both `isPublic` and the `publicId` you need next. Only a deliberate `PATCH {"isPublic":false}` — by you or by the human in the dashboard — ever closes an agent, and `{"isPublic":true}` reopens it. Write these two tags into the site yourself. You are in the codebase; the human is not, and asking them to paste something is the one step this whole guide exists to remove. Find the file that wraps every page — the root layout, the base template, whatever your framework calls it — and put both tags in it, just before ``. Use `publicId`, not `id`, in the second one. Then tell the human what you changed and where, the same way you would report any other edit. Hand the tags over only if you genuinely cannot place them: no repository in front of you, a site you do not have write access to, or a host where the markup is not yours to edit. In that case say plainly that the human has to paste them, and say exactly which file to paste them into. Check your own work. On their site, `window.agentstack` must be defined, the request for `embed.js` must return 200, and the bubble must open and reply. Read the two failures apart. **No bubble at all** means the widget was refused before it drew: a wrong `publicId`, a closed agent, or a workspace already scheduled for deletion — all three answer the eligibility check with 204 and nothing renders. **A bubble that opens but never answers** is the other failure, and it is almost always a spent balance: chat returns 402 `INSUFFICIENT_CREDITS` the moment `creditsRemaining` reaches zero. Send one message through section 3 with your key to see the real error, because the widget will not show it to you. The widget is not part of the REST API and does not answer to your key. It keeps working after the human claims the workspace, when your key has stopped working. That is the point — see section 7. Warn them once the widget is up. The widget survives a claim; it does not survive the absence of one. An unclaimed workspace is deleted 7 days after sign-up, and the widget on their site dies with it — the bubble stops loading and nothing on the page says why. **You are the only warning they get.** No email goes out, because nobody verified the address. So report the embed, the deadline and the claim URL together, never the embed alone. One warning. Visitor messages spend the same credits your own test messages do, out of the same 25. Do this step last, once the agent is trained and tested — otherwise strangers spend the budget you still needed. ## 5. What you cannot do Every route outside the list above returns 403 `SANDBOX_NOT_PERMITTED`. This includes email and other channels, custom actions that call external URLs, published help pages, help centers, custom domains, contacts, topics, the inbox, billing, roles and API key management. The list is default-deny: a route module added to AgentStack in future is refused until somebody opens it. There is no way to revoke your own key. Nothing here can spend money. The workspace has no owner and no members until a human claims it. Read `https://www.agentstack.build/api/v1/openapi.json` for the full API. Routes you cannot reach are still in that document. ## 6. The 7-day clock Two things expire together, both 7 days after sign-up: - your API key; and - the workspace itself. A daily job deletes every unclaimed workspace, with everything in it. There is no warning email, because nobody verified the address. There is no recovery. A claim stops both. Tell the human early. You cannot read this clock. The sign-up response gave you `claim_expires_at` once and no endpoint reports it again. Keep it. ## 7. Handing over: the claim The human reads the 6-digit code from their email. If they give it back to you, post it — with your API key, because this endpoint spends the workspace's five attempts and only its own agent may do that: curl -sS -X POST https://www.agentstack.build/api/v1/agent/verify \ -H "Authorization: Bearer $AGENTSTACK_API_KEY" \ -H 'content-type: application/json' \ -d '{"organization_id":"","code":"418207"}' A correct code answers `"claimed": false` and `"email_verified": true`. That is not a mistake. It confirms the address is real and the human read it, and it hands you the claim URL — but it does not take ownership, because a workspace needs an owner and this endpoint has no signed-in person to make one. The last step always happens in a browser. Tell the human to open the claim URL. Holding the code does not let you claim the workspace, and it is not meant to. Ownership is taken in a browser by somebody signed in as the address the code was sent to. You are not that person, so you cannot adopt the workspace or delete it, whatever the human tells you. Wrong, expired and unknown codes all return the same 400 `INVALID_CLAIM_CODE`. Five wrong codes lock the code out; a correct code never costs an attempt. The human can send themselves a new code from the claim page — you cannot. Until they claim, the workspace stays on `sandbox` and your key keeps working. **A claim moves the workspace from `sandbox` to `free`, and the free plan has no REST API access.** Your key stays valid and stops expiring, but from that moment every endpoint answers 403 `PLAN_UPGRADE_REQUIRED` until the owner upgrades to a paid plan. Read that as the end of your access, not the end of the work. **The widget keeps working.** It is a separate surface with no plan gate, so the agent you built goes on answering visitors on the `free` plan. For the human the claim is what keeps the work. For you it is the end. A claim grants no credits. Your 25 carry over and that is the whole balance: `free`'s 50 credits go to a workspace a person creates, `applySandboxClaim` does not add them, and `free` has no monthly refill. Visitors keep spending the same 25, so tell the human what the balance was when you left. So order your work: build, train, test, put the widget live, and only then ask for the claim. Anything you still need the API for must happen before you ask. Tell the human both halves — that the widget survives, and that `Hobby` at $30 a month is what brings the API back if they want an agent like you to keep working on it. The claim URL opens a page that shows the human what you built — the agent name, its instructions, what it was trained on, and any action endpoints. Your API key is never shown there. From that page they can also delete the workspace and everything in it at once. They sign in with the address you named; an account under any other address cannot claim this workspace. ## 8. Limits and failures 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 and needs your API key; without it the answer is 401. A platform-wide daily cap also applies. Over any limit the answer is 429 with a `Retry-After` header in seconds. Wait that long. Do not retry faster. If the rate limiter itself is unavailable, both endpoints answer 503 `LIMITER_UNAVAILABLE` and refuse the request. They fail closed on purpose. Retry later. ## 9. More - Guide for humans: https://www.agentstack.build/docs/get-started/agent-sandbox - API reference: https://www.agentstack.build/docs/developers/rest-agent-signup - Putting the widget on a site: https://www.agentstack.build/docs/guides/embed - All documentation: https://www.agentstack.build/docs/llms.txt