# Leads

Contact details people chose to give.

**A lead is another person's email address or phone number.** They gave it to
your automation; treat it as the personal data it is. Reading leads needs the
`leads:read` scope, which is in no preset.

```json
{
  "id": "f5b3d7e2-8a41-4c96-b0e7-6d2a9c1f3e58",
  "account_id": "a3d9f1c4-7b2e-4c60-9e85-1f4b6d2a0c73",
  "automation_id": "d2a6f8b1-5c94-4e07-9b3a-1e7c0f4d6a25",
  "network": "instagram",
  "handle": "roadrunner",
  "name": "R. Runner",
  "email": "roadrunner@example.com",
  "phone": null,
  "source": "comment_to_dm",
  "created_at": "2026-10-01T09:16:40+00:00"
}
```

## Operations

### `GET /v1/leads`

**List leads**

Scope `leads:read`.

Contact details people chose to give, newest first. Needs `leads:read`, which no preset includes.

**Parameters**

| Name | In | Type |  |
|---|---|---|---|
| `account_id` | query | `uuid` |  |
| `limit` | query | `integer` | Page size, 1 to 200. Above 200 is clamped; anything else is the default. (default `50`; >= 1; <= 200) |
| `cursor` | query | `string` | The `next_cursor` from the previous page. Opaque; an invalid one is `400`. (max 512) |

**Answers**

| Status | Body |  |
|---|---|---|
| 200 | `LeadList` | A page of leads. |
| 400 | `ErrorEnvelope` | `VALIDATION_ERROR`: a body that is not JSON, a field that breaks its rule (named in `fields`), a bad query value or cursor, or a malformed `Idempotency-Key`. |
| 401 | `ErrorEnvelope` | `UNAUTHORIZED`: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | `ErrorEnvelope` | `INSUFFICIENT_SCOPE`: the key lacks this operation's scope. Or `ORGANISATION_SUSPENDED`: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 429 | `ErrorEnvelope` | `RATE_LIMITED`: too many requests. Wait for `Retry-After` seconds. |
| 500 | `ErrorEnvelope` | `INTERNAL_ERROR`: ours. Safe to retry; send the same `Idempotency-Key` on a POST. |

**Captured leads** — answers `200`

Behind `leads:read`, which is in no preset: a lead is somebody else's address, given to your automation and not to every tool you connect.

```bash
curl "https://api.boltclaw.ai/v1/leads?limit=50" \
  -H "Authorization: Bearer bc_live_EXAMPLExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

```js
const res = await fetch("https://api.boltclaw.ai/v1/leads?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.BOLTCLAW_KEY}`,
  },
});

// 200
const data = await res.json();
```

### `DELETE /v1/leads/{id}`

**Delete a lead**

Scope `leads:read`. Also `social:write`. Another workspace's answers `404`.

Deletes the lead's contact details. Needs `leads:read` and `social:write` together.

**Parameters**

| Name | In | Type |  |
|---|---|---|---|
| `id` | path | `uuid` | An id this API gave you. Anything that is not one of yours is `404`. (**required**) |

**Answers**

| Status | Body |  |
|---|---|---|
| 200 | `Deleted` | Deleted. |
| 401 | `ErrorEnvelope` | `UNAUTHORIZED`: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | `ErrorEnvelope` | `INSUFFICIENT_SCOPE`: the key lacks this operation's scope. Or `ORGANISATION_SUSPENDED`: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 404 | `ErrorEnvelope` | `NOT_FOUND`: no such object, or it belongs to another workspace. The two are deliberately the same answer and never 403. If the id is right, check you are using the right key. |
| 428 | `ErrorEnvelope` | `APPROVAL_REQUIRED` on the first try, `APPROVAL_PENDING` on a retry before a person has decided. NOTHING HAS HAPPENED either way. The body carries `approval` with a `url`: show it to the person, and once they approve, send the identical request again with the approval id in `BoltClaw-Approval`. Only an AI assistant's request is ever answered this way. |
| 429 | `ErrorEnvelope` | `RATE_LIMITED`: too many requests. Wait for `Retry-After` seconds. |
| 500 | `ErrorEnvelope` | `INTERNAL_ERROR`: ours. Safe to retry; send the same `Idempotency-Key` on a POST. |

## Shapes

The objects these operations send and answer.

### Deleted

| Field | Type |  |
|---|---|---|
| `deleted` | `true` | (**required**) |
| `id` | `uuid` | (**required**) |

### Lead

| Field | Type |  |
|---|---|---|
| `id` | `uuid` | (**required**) |
| `account_id` | `uuid \| null` | (**required**) |
| `automation_id` | `uuid \| null` | (**required**) |
| `network` | `any` | (**required**) |
| `handle` | `string \| null` | (**required**) |
| `name` | `string \| null` | (**required**) |
| `email` | `string \| null` | (**required**) |
| `phone` | `string \| null` | (**required**) |
| `source` | `string` | (**required**; one of `comment_to_dm`, `dm`, `api`) |
| `created_at` | `date-time` | (**required**) |

### LeadList

| Field | Type |  |
|---|---|---|
| `data` | `Lead[]` | (**required**) |
| `next_cursor` | `NextCursor` | Pass back as `cursor` for the next page; null on the last page. (**required**) |

### NextCursor

Pass back as `cursor` for the next page; null on the last page.

`string | null`.

<!-- https://boltclaw.ai/docs/api/leads as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
