---
title: "Agents"
description: "Create an agent with its own handle, email address and keys — and suspend or reinstate it."
canonical_url: "https://www.agent-identity.dev/docs/concepts/identities"
markdown_url: "https://www.agent-identity.dev/docs/concepts/identities.md"
last_updated: "2018-10-20"
x_farming_labs_generated_preamble: true
agent:
  tokenBudget: 800
  task: "Create an agent with its own mailbox, then read it back or suspend it."
  outcome: "POST /v1/identities returns an agent with a handle and a provisioned mailbox_address that can send and receive mail."
  appliesTo:
    package:
      - "@aid/api"
      - "@agentidentity/sdk"
  prerequisites:
    - "An org-scoped API key; creating agents is an administrative operation."
    - "A verified sending domain if the mailbox must deliver outside the instance."
  files:
    - "apps/api/src/services/identity.ts"
  sideEffects:
    - "Provisions a real mailbox with the configured mail provider."
    - "Handles are unique across the whole service (case-insensitive) and cannot be reused while the agent exists. The handle is also the mailbox local part and the agent's A2A address."
  verification:
    - description: "Read the agent back."
      expect: "GET /v1/identities/{id} returns status active and a non-empty mailbox_address."
  rollback:
    - "POST /v1/identities/{id}/suspend stops sending and receiving without deleting history; reinstate restores it."
  failureModes:
    - symptom: "The handle is rejected as already taken."
      resolution: "Handles are unique across the whole service, including other organizations' and suspended agents. Choose another, or reinstate the existing one."
    - symptom: "The agent is created but mail is never delivered."
      resolution: "Sending is asynchronous: a 202 means queued. Watch for mail.sent and mail.bounced events, and verify your sending domain if you use a custom one."
---

# Agents
URL: /docs/concepts/identities
LLM index: /llms.txt
Description: Create an agent with its own handle, email address and keys — and suspend or reinstate it.
Related: /docs/getting-started/authentication, /docs/mail/sending, /docs/vault

<!-- farming-labs:agent-contract:start -->
## Agent Contract

Task: Create an agent with its own mailbox, then read it back or suspend it.
Outcome: POST /v1/identities returns an agent with a handle and a provisioned mailbox_address that can send and receive mail.

### Applies To

- Package: `@aid/api`, `@agentidentity/sdk`

### Prerequisites

- An org-scoped API key; creating agents is an administrative operation.
- A verified sending domain if the mailbox must deliver outside the instance.

### Files

- `apps/api/src/services/identity.ts`

### Side Effects

- Provisions a real mailbox with the configured mail provider.
- Handles are unique across the whole service (case-insensitive) and cannot be reused while the agent exists. The handle is also the mailbox local part and the agent's A2A address.

### Verification

- Read the agent back.
  - Expected: GET /v1/identities/{id} returns status active and a non-empty mailbox_address.

### Rollback

- POST /v1/identities/{id}/suspend stops sending and receiving without deleting history; reinstate restores it.

### Failure Modes

- The handle is rejected as already taken. — Recovery: Handles are unique across the whole service, including other organizations' and suspended agents. Choose another, or reinstate the existing one.
- The agent is created but mail is never delivered. — Recovery: Sending is asynchronous: a 202 means queued. Watch for mail.sent and mail.bounced events, and verify your sending domain if you use a custom one.
<!-- farming-labs:agent-contract:end -->

# Agents

Every agent has a handle, a display name, a mailbox that is provisioned
the moment it is created, zero or more API keys, and optionally a vault public key.

## Creating an agent with its own email address

```bash title="terminal"
curl -sS -X POST https://api.agent-identity.dev/v1/identities \
  -H "authorization: Bearer $AID_KEY" \
  -H 'content-type: application/json' \
  -d '{"handle":"support-agent","display_name":"Support Agent"}'
```

```json
{
  "id": "6f21…",
  "handle": "support-agent",
  "status": "active",
  "mailbox_address": "support-agent@yourdomain.com",
  "vault_public_key": null
}
```

The mailbox is not a separate resource you create afterwards — agent and mailbox come
into existence together, because an agent without an address is not much of an agent. The
address is derived from the handle and the configured sending domain.

`vault_public_key` stays `null` until the agent registers one; see [Vault](/docs/vault).

## Reading them back

`GET /v1/identities` lists every agent in the organization with its handle, display name,
status, mailbox address, and vault public key. `GET /v1/identities/:id` returns one.

## Renaming

Change the display name with `PATCH /v1/identities/:id`. The handle and mailbox address stay
the same. An org key can rename any agent; an agent key can rename only its own agent.

```bash title="terminal"
curl -sS -X PATCH "https://api.agent-identity.dev/v1/identities/$AID_AGENT" \
  -H "authorization: Bearer $AID_KEY" \
  -H 'content-type: application/json' \
  -d '{"display_name":"Support Desk"}'
```

```ts title="rename.ts"
await client.identities.update(agent.id, { displayName: "Support Desk" });
```

Names are 1–100 characters. The change is recorded in the audit log as `identity.updated`.

## Suspension

Suspension is the kill switch for a misbehaving agent. It is reversible and it is
recorded, which matters when you need to explain later what was stopped and why.

```bash title="terminal"
curl -sS -X POST "https://api.agent-identity.dev/v1/identities/$AID_AGENT/suspend" \
  -H "authorization: Bearer $AID_KEY" \
  -H 'content-type: application/json' \
  -d '{"reason":"Sending to unverified recipients"}'
```

`reason` is required — a suspension with no stated cause is not useful to whoever reads
the audit trail. `POST /v1/identities/:id/reinstate` lifts it.
`GET /v1/identities/suspensions` returns the history, each entry carrying `reason`,
`suspended_by`, `suspended_at`, and the nullable `reinstated_at` / `reinstated_by`.

Both operations require an org key. An agent cannot suspend or reinstate itself.

- `handle` and `display_name` are both required and both `min(1)`. A blank display name is
  a `400`, not a fallback to the handle.
- `POST /v1/identities` returns `identitySummarySchema` (`id`, `handle`, `status`,
  `mailbox_address`, `vault_public_key`). `GET /v1/identities/:id` returns
  `identityDetailSchema`, which has `display_name` but **not** `mailbox_address`. If you
  need both, read the list endpoint or keep the create response.
- There is no `PATCH /v1/identities/:id` and no `DELETE /v1/identities/:id` on the HTTP
  surface. `IdentityService.delete` exists in `apps/api/src/services/identity.ts` but is
  not routed. Suspension is the intended way to take an agent out of service.
- Suspension state shows up as `status` on the agent; do not infer it from the presence
  of rows in `/v1/identities/suspensions`, which is a full history including reinstated
  ones.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
