> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lupin.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent registration

> Create a bounded Free workspace, inbox and API key directly from an agent, without an existing account or human email step.

# Register an agent

An agent can call `POST https://api.lupin.sh/v0/agent/register` without a bearer token, console login or human email address. Registration creates a new Free workspace, default project, inbox and workspace API key. It does not create a human console user or give access to another workspace.

The initial key can receive and read email and create drafts. Outgoing email and paid resources are disabled. Registration does not send a verification message, buy a number, launch a browser or start compute.

## Save the retry secret first

`Idempotency-Key` is required: 32–256 letters, digits, underscores or hyphens. Generate an unguessable value and save it **before** the first request. This secret can retrieve the original API key on an identical replay, so protect it like the API key itself.

This shell example stores credentials outside your checkout. It needs `curl`, `openssl` and `jq`. Do not enable shell tracing or publish the response file.

```bash theme={null}
set -eu
umask 077
LUPIN_STATE="$HOME/.local/state/lupin/research-agent"
mkdir -p "$LUPIN_STATE"
chmod 700 "$LUPIN_STATE"
if [ ! -s "$LUPIN_STATE/registration-key" ]; then
  openssl rand -hex 32 > "$LUPIN_STATE/registration-key"
fi
export LUPIN_REGISTRATION_KEY="$(cat "$LUPIN_STATE/registration-key")"

curl --fail-with-body --silent --show-error \
  https://api.lupin.sh/v0/agent/register \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $LUPIN_REGISTRATION_KEY" \
  -d '{"username":"research-agent"}' \
  -o "$LUPIN_STATE/registration.pending.json"
jq -e '.api_key and .organization_id and .inbox_id' \
  "$LUPIN_STATE/registration.pending.json" > /dev/null
mv "$LUPIN_STATE/registration.pending.json" "$LUPIN_STATE/registration.json"
export LUPIN_API_KEY="$(jq -r .api_key "$LUPIN_STATE/registration.json")"
```

Use the same secret and username after a timeout or lost response. A successful creation returns `201`; an identical replay returns `200` with the original workspace and key. Do not generate a different secret just because a request failed.

## Read the result

The JSON response includes these fields:

```json theme={null}
{
  "organization_id": "org_example",
  "project_id": "pod_example",
  "inbox_id": "in_example",
  "email": "assigned-address@example.test",
  "api_key": "REDACTED",
  "verification_pending": true,
  "mode": "autonomous",
  "plan": "Free",
  "capabilities": {
    "receive_email": true,
    "read_email": true,
    "create_drafts": true,
    "send_email": false,
    "paid_resources": false
  },
  "limits": {
    "registrations_per_ip_per_day": 5,
    "registrations_global_per_day": 100,
    "inboxes": 3,
    "domains": 0
  },
  "links": {
    "console": "https://app.lupin.sh",
    "docs": "https://lupin.sh/agents",
    "claim": "https://app.lupin.sh/claim-agent"
  }
}
```

Use the returned `email`; the username is a display/registration label, not a guarantee of a particular address. Store the response as a credential and never place its API key in a URL, public prompt or source file.

You can immediately inspect your own inbox:

```bash theme={null}
INBOX_ID="$(jq -r .inbox_id "$LUPIN_STATE/registration.json")"
curl --fail-with-body --silent --show-error \
  "https://api.lupin.sh/v0/emails/inboxes/$INBOX_ID" \
  -H "Authorization: Bearer $LUPIN_API_KEY"
```

## Registration errors and retries

| Response | Meaning | Next action |
| - | - | - |
| `400 validation_error` | Invalid username or request body | Use only `username`, with 3–40 letters, digits or hyphens, starting and ending with a letter or digit. |
| `400 invalid_idempotency_key` | Missing or invalid retry secret | Save a strong key before calling. |
| `409 idempotency_conflict` | The secret belongs to a different request | Restore the original username and secret; do not overwrite an existing identity. |
| `409 signup_key_revoked` | The original key was revoked | Stop replaying; recover access through an existing authorized workspace owner/key. |
| `429 registration_rate_limited` | A creation quota is exhausted | Honor `Retry-After`. Limits are 5 new registrations per IP and 100 globally per UTC day. |
| `503 registration_unavailable` / `persistence_failed` | Registration cannot safely finish | Preserve the secret and retry later. |

Existing-key API calls have their own [rate limits](https://docs.lupin.sh/platform/rate-limits.md), permissions and budgets. New registrations do not reset those controls for an existing workspace.

## SDK, CLI and MCP

The checked-in [TypeScript client](https://docs.lupin.sh/build/sdk.md) exposes `agent.register({ username }, savedSecret)`. The [CLI](https://docs.lupin.sh/build/cli.md) exposes `agent register --username NAME`, reading the saved secret from `LUPIN_REGISTRATION_KEY` or `--idempotency-key`. Both preserve the same retry identity and return the real API response; they do not retry automatically with a new secret.

The SDK and CLI are workspace source packages. Public npm installation is not required for registration; the HTTPS example above is sufficient for an agent without repository access.

[MCP](https://docs.lupin.sh/build/mcp.md) requires a workspace bearer key. Register through HTTPS first, then configure `https://api.lupin.sh/mcp` with the returned key and discover tools with `tools/list`. No anonymous MCP registration or OAuth bootstrap is implied.

## Human accounts and later access

Human console accounts and agent workspaces are separate. The owner opens [Claim an agent workspace](https://app.lupin.sh/claim-agent), signs in with a verified human email, and submits the registration key in the private console form. No credential belongs in the link or query string.

The claim links the workspace to that human and creates an owner credential held server-side by the console. The original agent key keeps its restrictions. The owner explicitly grants any additional agent access through the console's API-key controls; claiming does not silently upgrade the original key or authorize a payment, call or outgoing message.

The existing `POST /v0/agent/sign-up` plus `/verify` flow remains available when a human email address and its verification code are part of the intended onboarding. It is separate from the autonomous-registration claim process.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.