---
title: "Agent self-onboarding (atomic)"
method: POST
path: "/api/agents/register"
tags: ["agents"]
---

# Agent self-onboarding (atomic)

`POST /api/agents/register`

## Request

- Base URL: `https://api.depixapp.com`
- URL: `https://api.depixapp.com/api/agents/register`
- Auth: none required

Creates an agent account + merchant + sandbox key + starter live key in one atomic transaction. Authentication is the agent keypair scheme via signed headers (X-Agent-Public-Key / X-Agent-Timestamp / X-Agent-Nonce / X-Agent-Signature — Ed25519 over the canonical string `depix-agent-auth:v1\napi.depixapp.com\n{METHOD}\n{PATH}\n{timestamp}\n{nonce}\n{sha256hex(canonical-json-body)}` — the body hash is over the compact JSON serialization), NOT a Bearer key — plus the REQUIRED operator_token anti-farm anchor. Key plaintexts and the webhook secret are returned ONLY here.

## Request body

- AgentRegisterRequest: Agent registration body. The request must ALSO carry the signed agent headers (X-Agent-Public-Key, X-Agent-Timestamp, X-Agent-Nonce, X-Agent-Signature — Ed25519 over the canonical string; the public key becomes the account identity).
  - `name` string, required: Display/business name (2–100 chars). Becomes the merchant business_name.
  - `username` string: Optional handle (3–30 chars, [a-zA-Z0-9_]). Defaults to agent_<first 10 hex of the public key>. Taken → 409 username_taken.
  - `operator_token` string, required: REQUIRED anti-farm anchor: the op_… token minted when the human operator connected via OAuth or the GitHub PAT fast-path. One operator token clusters N agents.
  - `operator_email` string, required: Operator notification email (format-validated only; never becomes the account login email).
  - `liquid_address` string, required: Static Liquid address for merchant receipts — produced by the agent's own wallet (the SDK derives it; not user-chosen). Immutable after registration.
  - `default_callback_url` string: Optional HTTPS webhook endpoint for deposit.*/withdraw.*/checkout.* events.
  - `ref` string: Optional referrer username for affiliate attribution.

## Response `201`

Agent created — store the key plaintexts and webhook secret now; they are never shown again.

- AgentRegisterResponse: Agent onboarding result — the ONLY response that ever carries key plaintexts and the webhook secret.
  - `response` object, required: Envelope (legacy contract shape).
    - `agent` object, required: The created agent account.
      - `username` string, required: Account handle (also the merchant slug until graduation).
      - `public_key` string, required: The Ed25519 public key registered as the account identity (64 hex).
      - `account_type` 'agent', required: Always "agent" — server-set and immutable.
    - `merchant` object, required: The auto-created merchant profile.
      - `id` string, required: Merchant id (mrc_…).
      - `merchant_slug` string, required: Public page slug — equals the username until graduation (anti-squat).
      - `liquid_address` string, required: The static receiving address from the request.
      - `webhook_secret` string, required: ONE-TIME plaintext webhook signing secret (whsec_…). Rotatable later; never shown again.
    - `keys` object, required: Auto-issued API keys — plaintexts shown ONLY here.
      - `test` object, required: Sandbox key with the full default scopes.
        - `id` string, required: Key id.
        - `key` string, required: sk_test_… plaintext (one-time).
        - `scopes` string, required: Space-separated scopes granted to the sandbox key.
      - `live_starter` object, required: Pre-graduation production key: wallet-only scopes with mandatory low limits.
        - `id` string, required: Key id.
        - `key` string, required: sk_live_… plaintext (one-time).
        - `scopes` string, required: Exactly "wallet_read wallet_write" — merchant scopes require graduation + a verified domain.
        - `per_tx_limit_cents` integer, required: Mandatory per-transaction cap on this key.
        - `daily_limit_cents` integer, required: Mandatory rolling daily cap on this key.
        - `starter` boolean, required: Always true — marks the auto-issued starter key.
    - `graduation` object, required: What unlocks full production capability, and where to do it. Graduation is downstream of verification, and an AGENT account verifies by proving control of a registrable domain over DNS — never by settling a number of deposits. Deposits buy nothing here; the two calls below are the whole path.
      - `requires` 'domain_proof', required: The gate, as a machine-readable token — the same one GET /api/verification reports in `missing[]`/`steps[]` and GET /api/agents/status reports in `graduation.blocked_on`.
      - `verify_domain_endpoint` string, required: Method + path of the call that performs the proof, in two phases: `{domain}` returns the TXT record to publish, `{domain, confirm: true}` resolves it. A successful proof verifies the account, and graduation follows on its own (watch `graduation.blocked_on` on GET /api/agents/status, or the `agent.graduated` webhook).
      - `allowed_tlds_endpoint` string, required: Method + path of the public TLD allowlist. Call it before picking a domain — it is what verify-domain will accept, and it avoids a trial-and-error 422.
    - `pacing` object, required: Read-only account limits so the agent can pace itself without trial-and-error 4xx.
      - `first_deposit_max_cents` integer, required: Cap on the account's first deposit.
      - `unverified_per_tx_max_cents` integer, required: Per-deposit cap before verification.
      - `inter_deposit_delay_hours` integer, required: MINIMUM settlement delay applied to deposits 2–5 of ANY account — verified or not, personal deposit or Pix checkout, since the account has one deposit ledger. The DePix payout of each of those deposits is held AT LEAST this many hours (deposit status `delayed`). It is a floor, not the whole answer: the rules that hold a deposit compose by taking the LONGEST, so a deposit 2–5 too large to count as pacing, or one under risk review, is held for the account's trust-level window instead — which is longer. Read `vault_window_hours` on GET /api/vault/status and pace against the greater of the two before promising anyone a release time. Deposit 1 settles immediately and deposit 6 onward is governed by the receive cap and the instant lane. It does NOT block CREATING the next deposit — you never have to wait between creates because of this field.
      - `payer_velocity` object, required: Per-payer deposit velocity gate.
        - `max_per_window` integer, required: Deposits allowed per payer per window.
        - `window_minutes` integer, required: Window length in minutes.
      - `verified_per_tx_deposit_max_cents` integer, required: Per-deposit cap after verification, on `amountInCents` — what the payer sends on POST /api/deposit.
      - `verified_per_tx_withdraw_send_max_cents` integer, required: Per-withdrawal cap after verification, on `depositAmountInCents` — the DePix the wallet sends. Mirrors the request schema maximum.
      - `verified_per_tx_withdraw_receive_max_cents` integer, required: Per-withdrawal cap after verification, on `payoutAmountInCents` — the BRL the destination key receives. Mirrors the request schema maximum; the provider bounds each request field at the same figure, so this equals the send cap.

## Other responses

- `400`: Invalid field (validation_error with details.field).
- `401`: Signature chain failed (agent_invalid_signature / agent_signature_expired with details.server_time / agent_replay_detected) or unknown operator token (invalid_operator_token).
- `403`: Revoked operator token (operator_token_revoked) or a blocked public key (registration_blocked).
- `409`: Public key already registered (agent_pubkey_exists) or explicit username in use (username_taken).
- `429`: Rate limited by one of the two anti-farm caps: registrations from this IP in 24h (`rate_limited`, `details.scope` = "agent-register-daily"), or agent accounts registered under this OPERATOR TOKEN in a rolling 24h window (`operator_register_cap_exceeded`, default 5). Retry after `retry_after` seconds (also in the Retry-After header); an operator cap you did not cause means the token has leaked and should be revoked.
- `503`: A registration guard could not run, so it refused: the nonce anti-replay store, or the per-IP cap that shares it, was unavailable. Both fail CLOSED — retry.

---

[API](https://skmtc.dev/depixapp/apis/depix-api.md) · [All operations](https://skmtc.dev/depixapp/apis/depix-api/llms.txt) · [OpenAPI document](https://skmtc.dev/depixapp/apis/depix-api/revisions/04094536dd0f?raw)
