# Agent self-signup

Source: https://agentboxd.com/docs/agent-signup

> An agent with no API key can create its own workspace and inbox with one call. A human claims it later to lift the limits.

An AI agent doesn’t need a person to get started. It can create its own Agentboxd workspace, with one inbox and an API key, by solving a short proof-of-work challenge. The workspace is **unclaimed**: it can receive mail freely and send a little, and a human can **claim** it later to become its owner and lift the limits.

## Sign up from code

The SDKs fetch the challenge, solve it (a few seconds of CPU) and create the workspace. The key is returned once, so store it right away.

agent.ts:

```ts
import { Agentboxd } from 'agentboxd';

// No API key: solves a short proof-of-work challenge, then creates the workspace.
const { client, api_key, inbox } = await Agentboxd.signup({
  agentName: 'research-agent',
  ownerEmail: 'you@example.com', // optional: gets a link to claim the workspace
});
// Store api_key now (e.g. as AGENTBOXD_API_KEY): it is shown only once.

const mail = await client.messages.wait(inbox.id, { timeout: 60 });
```

agent.py:

```python
from agentboxd import Agentboxd

s = Agentboxd.signup(agent_name="research-agent", owner_email="you@example.com")
print(s.api_key)  # shown only once: store it
mr = s.client     # already uses the new key
mail = mr.messages.wait(s.inbox["id"], timeout=60)
```

Without an SDK, it is two calls:

curl:

```bash
# 1. A challenge (no key needed)
curl https://api.agentboxd.com/v1/signup/challenge
# → { "challenge": "v1.eyJu...", "algorithm": "sha256", "difficulty": 21, "expires_at": "...", ... }

# 2. Find a decimal "solution" so SHA-256(challenge + ":" + solution) starts with 21 zero bits,
#    counting up from 0 (the SDKs do this). Then:
curl -X POST https://api.agentboxd.com/v1/signup \
  -H "Content-Type: application/json" \
  -d '{ "challenge": "v1.eyJu...", "solution": "1830211", "agent_name": "research-agent", "owner_email": "you@example.com" }'
```

201 Created:

```json
{
  "api_key": "mr_8Hq2...",
  "workspace": { "id": "0b6c…", "name": "research-agent's workspace", "status": "unclaimed", "created_at": "…", "claimed_at": null },
  "inbox": { "id": "6f1c…", "address": "brisk-otter-4821@homingbox.net", … },
  "claim": { "status": "email_sent", "email": "y***@example.com" },
  "restrictions": {
    "recipients_per_day": 20,
    "replies_to_inbound_threads": "unlimited",
    "inboxes": 3,
    "identities": 3,
    "webhooks": false,
    "custom_domains": false,
    "event_stream": true,
    "expires_after_inactive_days": 30
  },
  "docs_url": "https://agentboxd.com/docs/agent-signup",
  "next_steps": ["Store api_key now: it is shown only once. It is scoped to this inbox.", "…"]
}
```

The key is scoped to the new inbox, with the **Send & read** permissions plus `webhooks:manage` (for when the workspace is claimed). It can’t create more inboxes, manage domains or mint [identity tokens](https://agentboxd.com/docs/agent-identity).

### Sign up as an identity without a mailbox

An agent that only needs to sign in to apps can pass `kind: "identity"` (`Agentboxd.signup({ kind: 'identity' })`, `kind="identity"` in Python, or the MCP tool’s `kind`). It gets an [identity without a mailbox](https://agentboxd.com/docs/agent-identity#identity-without-a-mailbox) instead of an inbox: the response has `identity` and `inbox: null`, and the key is scoped to the identity with `inboxes:read`, `identity:sign`, `messages:send` (only to request the claim; the identity can’t send mail) and `webhooks:manage`. Identity tokens are refused (`403 unclaimed_workspace`) until a person claims the workspace; then the same key signs in.

## Sign up from an MCP client

Start `@agentboxd/mcp` without `AGENTBOXD_API_KEY`. The agent sees a `signup` tool; after calling it, the same session uses the new key. **Nothing is written to disk by default**: the tool returns the key and tells the agent to hand it to you. Start the server with `--save-key=<file>` to keep it across restarts.

claude_desktop_config.json:

```json
{
  "mcpServers": {
    "agentboxd": {
      "command": "npx",
      "args": ["-y", "@agentboxd/mcp"]
    }
  }
}
```

## What an unclaimed workspace can do

|  | Unclaimed | After a claim (Free plan) |
| --- | --- | --- |
| Receiving | Unlimited, as on every plan (inbound mail is never refused) | Same |
| Sending | Replies in threads that someone started by emailing the agent, to people who wrote in that thread from an address that passed DMARC: not limited. Anything else: at most **20 different recipients per UTC day** | Free plan limits |
| Scheduled sends | At most 24 hours ahead, and the scheduled drafts together reach at most 20 different people (replies to people who wrote in are not counted): `429 unclaimed_schedule_limit` | Up to 30 days ahead |
| Bounces | Sending pauses after 3 hard bounces if they are 10% or more of the sends so far | Over 5% of at least 20 sends in 7 days (also after a claim, for the first 14 days) |
| Inboxes | 3: the one created at signup, and more with `POST /v1/signup/agents`, each with its own key | 10 |
| Identities without a mailbox | 3 (with `kind: "identity"`) | 100 |
| Webhooks | No (`403 unclaimed_workspace`); the [event stream](https://agentboxd.com/docs/realtime-events) works | Yes |
| Custom domains | No | 1 |
| Runboxd computers | No | 1, with 20 hours a month |
| Sign in with Agentboxd | No identity tokens | Yes, with a key that has `identity:sign` |
| Kept for | 30 days after the last activity (API use or mail), then deleted | Your plan’s retention |

The burst limit, the daily caps and the monthly email quota of the Free plan apply as well. A send over the recipient limit gets `429 unclaimed_recipient_limit` with `details: { limit, used, retry_after_seconds }` and a `Retry-After` header until 00:00 UTC. `GET /v1/account` (or `client.account.get()`, or the `get_account` MCP tool) shows the claim status, the effective limits, today’s recipient count and when an idle workspace would be deleted.

## Claiming a workspace

Pass `owner_email` at signup, or call `POST /v1/signup/claim { email }` later (at most 3 a day). We email that person a single-use link from `login@agentboxd.com`, valid 72 hours. They open it, review what they are claiming and confirm; that signs them in (like a sign-in link) and makes them the workspace’s owner. The workspace becomes a normal Free workspace and a `signup.claimed` event is recorded. The address must be the person’s own, outside Agentboxd.

The claim email contains only our own text, the inbox address and the link: nothing the agent wrote. If the person doesn’t know the agent, they can ignore it; nothing happens.

## Abuse controls

- **Proof of work.** Each challenge needs about two million SHA-256 hashes (a few seconds of one CPU core) and checking it takes one. Challenges are signed, expire after 10 minutes and work once.
- **Several agents, one workspace.** An owner who runs several agents keeps them in one workspace: call `POST /v1/signup/agents` with the key you already have (SDK `client.account.addAgent()`, MCP `add_agent`) to get another inbox with its own key, which sees only that inbox. Up to 3 agents until a person claims the workspace; after that the plan’s limits apply and a workspace key creates inboxes with `POST /v1/inboxes`. Don’t sign up again for the same owner: a second workspace for the same owner address is refused (`429 owner_limit_reached`).
- **Rate limits.** At most 5 signups per IP address per hour and 20 per network (IPv4 /24, IPv6 /56) per day: `429 signup_rate_limited` with `Retry-After`. One owner can have at most 1 unclaimed workspace per email address, and a company email domain (subdomains included) at most 3 owner addresses with unclaimed workspaces, from the last 24 hours (large free-mail providers: the per-address limit only): `429 owner_limit_reached`, at signup with `owner_email` and on `POST /v1/signup/claim`. One IP address can hold at most 5 unclaimed workspaces from the last 7 days, and one network 20: `429 unclaimed_limit_reached` with `Retry-After`. Claiming a workspace frees its place.
- **Sending limits until claimed**, above, on top of the usual bounce and complaint checks: a workspace whose mail bounces or draws complaints has its sending paused automatically, and is suspended for clear abuse.
- **Expiry.** Unclaimed workspaces with no activity for 30 days are deleted with their mail and files. Their addresses are never handed out again.
- **A kill switch.** The operator can turn signup off at any time: `503 signup_disabled`.

## Acceptable use

Self-signup follows the same rules as every workspace: no spam or unsolicited bulk mail, no phishing or impersonation, no mail to people who didn’t ask for it. Workspaces that break them are suspended and deleted. Report abuse to [abuse@agentboxd.com](mailto:abuse@agentboxd.com). Signing up accepts the [Terms of Service](https://agentboxd.com/legal/terms), which the person who claims the workspace accepts for it too.

## Errors

| Status & code | When |
| --- | --- |
| `400 invalid_solution` · `400 challenge_expired` · `400 challenge_used` | The solution doesn’t match, the challenge is older than 10 minutes, or it was already used. Get a new challenge. |
| `422 invalid_owner_email` | `owner_email` is an Agentboxd address. It must be a person’s own address. |
| `429 signup_rate_limited` | Too many signups from this IP or network. `details.scope` is `ip` or `network`. |
| `429 owner_limit_reached` | This owner address, or its company domain, already has as many unclaimed agent workspaces from the last 24 hours as allowed. Claim one first, or wait. |
| `429 unclaimed_limit_reached` | This IP address or network already holds as many unclaimed agent workspaces from the last 7 days as allowed. Add agents to one you have with `POST /v1/signup/agents`, or claim one. `details.scope` is `ip` or `network`. |
| `503 signup_disabled` | Self-signup is turned off on this server. |
| `429 unclaimed_recipient_limit` | The daily recipient allowance of an unclaimed workspace is used up. |
| `403 unclaimed_workspace` | Webhooks, custom domains, temporary inboxes and identity tokens need a claimed workspace. |
| `409 already_claimed` · `409 not_claimable` | A claim was requested for a workspace that already has an owner. |
