# Leads

> List and read the leads Couplr's assistant is talking to, and add a lead.

- Canonical URL: https://couplrfsm.com/developers/api-reference/leads
- Markdown: https://couplrfsm.com/developers/api-reference/leads.md
- API version: v1 (base URL https://couplrfsm.com/api/v1)
- OpenAPI specification: https://couplrfsm.com/developers/openapi.yaml
- All documentation pages: https://couplrfsm.com/llms.txt

> **Flip:** These are the conversations I have with new leads by text, web chat and phone, with what I learned from each. You can also hand me a lead. I will not text them unless you ask me to and tell me they agreed to it.

Base URL: `https://couplrfsm.com/api/v1`. Conventions shared by every endpoint (examples, ids, dates, money) are on the [API reference](https://couplrfsm.com/developers/api-reference) page. Errors, pagination and idempotency are in [Errors, limits and retries](https://couplrfsm.com/developers/errors-and-limits).

## List leads

`GET /api/v1/leads` · requires the `leads:read` ability

**Query parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | integer | No | Default: `1`. |
| `per_page` | integer | No | Values above 100 are served as 100. Default: `25`. |
| `updated_since` | string (date-time) | No | Only records changed at or after this time. ISO 8601, for example `2026-11-14T15:00:00Z`. |
| `channel` | string | No | Exact match, e.g. `sms`, `web_chat`, `voice`. |
| `status` | string | No |  |
| `created_from` | string (date) | No |  |

**Example request**

```bash
curl "https://couplrfsm.com/api/v1/leads" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Example response** (`200`)

```json
{
    "data": [
        {
            "id": "01a1db67-3ef9-73f3-bf60-4e522919bef6",
            "channel": "sms",
            "status": "active",
            "phone": "5552018890",
            "zip": "74105",
            "customer_id": 1842,
            "collected": {
                "issue": "No cooling upstairs"
            },
            "last_inbound_at": "2026-11-14T15:00:02+00:00",
            "last_outbound_at": "2026-11-14T15:00:09+00:00",
            "created_at": "2026-11-14T14:58:30+00:00",
            "updated_at": "2026-11-14T15:00:09+00:00"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "total": 1,
        "last_page": 1
    }
}
```

**Errors**

| Status | When |
| --- | --- |
| `401` | No key (`api_key_missing`), the key is unknown or revoked (`api_key_invalid`), the key had an end date that has passed (`api_key_expired`), or the key belongs to a private trial sandbox whose access window has ended (`sandbox_expired`). |
| `403` | The key lacks the ability this endpoint needs (`ability_missing`), no admin user is left to act for the key (`no_acting_admin`), or the company's account is not yet verified. |
| `422` | A parameter or field is invalid (`validation_failed`). `errors` maps each field to its messages. |
| `429` | Too many requests (`rate_limited`). Wait `Retry-After` seconds. |

## Create a lead

`POST /api/v1/leads` · requires the `leads:write` ability · accepts `Idempotency-Key`

Adds a lead. The person is matched to an existing customer by phone
number, or added as a new customer, and the lead appears in Speed to
Lead with a note for the office to follow up by phone.

**By default nobody is texted.** To have Couplr's assistant open a
text conversation with the person, send both
`"start_conversation": true` and `"sms_consent": true`. Send
`sms_consent` only when the person has agreed to receive texts from
this company. A conversation that ends with the assistant booking a
visit counts as one booked lead on the account's plan, the same as a
lead from the website form.

Even when asked, no text is sent if texting is not set up for the
account, the person has replied STOP before, or the number is not a
US or Canadian one. The lead is still created, and the response says
why in `conversation_not_started_reason`.

Each call creates a lead. Send an `Idempotency-Key` so a retry does
not create a second one.

**Headers**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | No | Makes a retry of this request safe. See "Retries and idempotency". |

**Request body** (`application/json`)

Fields not listed are ignored.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes |  |
| `phone` | string | Yes | At least ten digits. Punctuation is ignored. |
| `zip` | string or null | No | Used to check the lead is inside the company's service area. |
| `start_conversation` | boolean | No | Have the assistant text this person to start a Speed to Lead conversation. Needs `sms_consent`. Default: `false`. |
| `sms_consent` | boolean | No | True only when this person has agreed to receive texts from the company. Required when `start_conversation` is true. Default: `false`. |

**Example request**

```bash
curl -X POST "https://couplrfsm.com/api/v1/leads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_KEY" \
  -d '{"name":"Dana Whitfield","phone":"555-201-8890","zip":"74105"}'
```

**Example response** (`201`)

```json
{
    "data": {
        "id": "01a1db67-3ef9-73f3-bf60-4e522919bef6",
        "channel": "sms",
        "status": "active",
        "phone": "5552018890",
        "zip": "74105",
        "customer_id": 1842,
        "collected": {
            "issue": "No cooling upstairs"
        },
        "last_inbound_at": "2026-11-14T15:00:02+00:00",
        "last_outbound_at": "2026-11-14T15:00:09+00:00",
        "created_at": "2026-11-14T14:58:30+00:00",
        "updated_at": "2026-11-14T15:00:09+00:00"
    }
}
```

**Errors**

| Status | When |
| --- | --- |
| `401` | No key (`api_key_missing`), the key is unknown or revoked (`api_key_invalid`), the key had an end date that has passed (`api_key_expired`), or the key belongs to a private trial sandbox whose access window has ended (`sandbox_expired`). |
| `403` | The key lacks the ability this endpoint needs (`ability_missing`), no admin user is left to act for the key (`no_acting_admin`), or the company's account is not yet verified. |
| `422` | A parameter or field is invalid (`validation_failed`). `errors` maps each field to its messages. |
| `429` | Too many requests (`rate_limited`). Wait `Retry-After` seconds. |

## Get a lead

`GET /api/v1/leads/{id}` · requires the `leads:read` ability

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | Yes |  |

**Example request**

```bash
curl "https://couplrfsm.com/api/v1/leads/9d3f6a52-0c1e-4c57-9d0a-3a0f8a4a7b21" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Example response** (`200`)

```json
{
    "data": {
        "id": "01a1db67-3ef9-73f3-bf60-4e522919bef6",
        "channel": "sms",
        "status": "active",
        "phone": "5552018890",
        "zip": "74105",
        "customer_id": 1842,
        "collected": {
            "issue": "No cooling upstairs"
        },
        "last_inbound_at": "2026-11-14T15:00:02+00:00",
        "last_outbound_at": "2026-11-14T15:00:09+00:00",
        "created_at": "2026-11-14T14:58:30+00:00",
        "updated_at": "2026-11-14T15:00:09+00:00"
    }
}
```

**Errors**

| Status | When |
| --- | --- |
| `401` | No key (`api_key_missing`), the key is unknown or revoked (`api_key_invalid`), the key had an end date that has passed (`api_key_expired`), or the key belongs to a private trial sandbox whose access window has ended (`sandbox_expired`). |
| `403` | The key lacks the ability this endpoint needs (`ability_missing`), no admin user is left to act for the key (`no_acting_admin`), or the company's account is not yet verified. |
| `404` | No such record in this company (`not_found`). |
| `429` | Too many requests (`rate_limited`). Wait `Retry-After` seconds. |

## Lead object

| Field | Type | Description |
| --- | --- | --- |
| `id` | string (uuid) |  |
| `channel` | string or null |  |
| `status` | string or null |  |
| `phone` | string or null |  |
| `zip` | string or null |  |
| `customer_id` | integer or null |  |
| `collected` | object or null | Whatever the assistant captured. Keys vary by lead. |
| `last_inbound_at` | string (date-time) or null |  |
| `last_outbound_at` | string (date-time) or null |  |
| `created_at` | string (date-time) or null |  |
| `updated_at` | string (date-time) or null |  |
