# Customers

> List, find, create and update customers, and add service addresses to them.

- Canonical URL: https://couplrfsm.com/developers/api-reference/customers
- Markdown: https://couplrfsm.com/developers/api-reference/customers.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:** Customers are the people and businesses you do work for. Everything else, jobs, invoices, estimates, hangs off a customer, so this is usually where an integration starts.

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 customers

`GET /api/v1/customers` · requires the `customers: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`. |
| `search` | string | No | Matches part of a name, alternate name, email or phone. |
| `email` | string | No | Exact email match. |
| `phone` | string | No | Matches part of the phone number; non-digits are ignored. |

**Example request**

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

**Example response** (`200`)

```json
{
    "data": [
        {
            "id": 1842,
            "name": "Dana Whitfield",
            "email": "dana@example.com",
            "phone": "5552018890",
            "phone_is_mobile": true,
            "addresses": [
                {
                    "id": 2210,
                    "address": "412 Larkspur Ln",
                    "city": "Tulsa",
                    "state": "OK",
                    "zip_code": "74105"
                }
            ],
            "created_at": "2026-11-14T15:02:11+00:00",
            "updated_at": "2026-11-14T15:02:11+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 customer

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

**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. `email` is required unless `uses_email` is `false`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes |  |
| `email` | string (email) or null | No |  |
| `uses_email` | boolean | No | `false` records that the customer has no email. |
| `phone` | string | Yes | Any format; stored as digits. |
| `phone_is_mobile` | boolean | No |  |
| `address` | string or null | No | Updates the customer's primary address. |
| `city` | string or null | No |  |
| `state` | string or null | No |  |
| `zip_code` | string or null | No |  |
| `sms_consent` | boolean | No | Send only when the customer has agreed to receive texts. `true` records consent only when none is on file, and never clears an opt-out the customer made by texting STOP; only the customer can undo that, by texting START. `false` opts the customer out. |
| `send_card_request` | boolean | No | `true` emails the customer a request to put a card on file. Default: `false`. |
| `ignore_duplicates` | boolean | No | `false` returns `409` with candidates when the name or address matches an existing customer. Default: `true`. |

**Example request**

```bash
curl -X POST "https://couplrfsm.com/api/v1/customers" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_KEY" \
  -d '{"name":"Dana Whitfield","email":"dana@example.com","phone":"(555) 201-8890","address":"412 Larkspur Ln","city":"Tulsa","state":"OK","zip_code":"74105"}'
```

**Example response** (`201`)

```json
{
    "data": {
        "id": 1842,
        "name": "Dana Whitfield",
        "email": "dana@example.com",
        "phone": "5552018890",
        "phone_is_mobile": true,
        "addresses": [
            {
                "id": 2210,
                "address": "412 Larkspur Ln",
                "city": "Tulsa",
                "state": "OK",
                "zip_code": "74105"
            }
        ],
        "created_at": "2026-11-14T15:02:11+00:00",
        "updated_at": "2026-11-14T15:02:11+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. |
| `409` | Only when `ignore_duplicates` is `false`: a customer with the same name or address exists. The body lists up to 5 candidates under `duplicates`, each a Customer plus `matched_on` (what collided). Nothing was created. |
| `422` | Validation failed, including an email (or, with `uses_email: false`, a phone) that already belongs to another customer. |
| `429` | Too many requests (`rate_limited`). Wait `Retry-After` seconds. |

## Get a customer

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

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes |  |

**Example request**

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

**Example response** (`200`)

```json
{
    "data": {
        "id": 1842,
        "name": "Dana Whitfield",
        "email": "dana@example.com",
        "phone": "5552018890",
        "phone_is_mobile": true,
        "addresses": [
            {
                "id": 2210,
                "address": "412 Larkspur Ln",
                "city": "Tulsa",
                "state": "OK",
                "zip_code": "74105"
            }
        ],
        "created_at": "2026-11-14T15:02:11+00:00",
        "updated_at": "2026-11-14T15:02:11+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. |

## Update a customer

`PATCH /api/v1/customers/{id}` · requires the `customers:write` ability · accepts `Idempotency-Key`

Only the fields you send change.

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes |  |

**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 | No |  |
| `email` | string (email) or null | No |  |
| `uses_email` | boolean | No | `false` records that the customer has no email. |
| `phone` | string | No | Any format; stored as digits. |
| `phone_is_mobile` | boolean | No |  |
| `address` | string or null | No | Updates the customer's primary address. |
| `city` | string or null | No |  |
| `state` | string or null | No |  |
| `zip_code` | string or null | No |  |
| `sms_consent` | boolean | No | Send only when the customer has agreed to receive texts. `true` records consent only when none is on file, and never clears an opt-out the customer made by texting STOP; only the customer can undo that, by texting START. `false` opts the customer out. |

**Example request**

```bash
curl -X PATCH "https://couplrfsm.com/api/v1/customers/1842" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_KEY" \
  -d '{"phone":"(555) 201-7700"}'
```

**Example response** (`200`)

```json
{
    "data": {
        "id": 1842,
        "name": "Dana Whitfield",
        "email": "dana@example.com",
        "phone": "5552018890",
        "phone_is_mobile": true,
        "addresses": [
            {
                "id": 2210,
                "address": "412 Larkspur Ln",
                "city": "Tulsa",
                "state": "OK",
                "zip_code": "74105"
            }
        ],
        "created_at": "2026-11-14T15:02:11+00:00",
        "updated_at": "2026-11-14T15:02:11+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`). |
| `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. |

## Add an address to a customer

`POST /api/v1/customers/{id}/addresses` · requires the `customers:write` ability · accepts `Idempotency-Key`

Adds another service address, such as a rental or a second property. Use its id as `address_id` when creating a job.

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes |  |

**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 |
| --- | --- | --- | --- |
| `address` | string | Yes | Street address. |
| `city` | string or null | No |  |
| `state` | string or null | No |  |
| `zip_code` | string or null | No |  |

**Example request**

```bash
curl -X POST "https://couplrfsm.com/api/v1/customers/1842/addresses" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_KEY" \
  -d '{"address":"88 Rental Row","city":"Tulsa","state":"OK","zip_code":"74104"}'
```

**Example response** (`201`)

```json
{
    "data": {
        "id": 2211,
        "address": "88 Rental Row",
        "city": "Tulsa",
        "state": "OK",
        "zip_code": "74104"
    }
}
```

**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`). |
| `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. |

## Customer object

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer |  |
| `name` | string |  |
| `email` | string or null |  |
| `phone` | string or null | Digits only. |
| `phone_is_mobile` | boolean |  |
| `addresses` | array of objects |  |
| `created_at` | string (date-time) or null |  |
| `updated_at` | string (date-time) or null |  |

## Address object

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer |  |
| `address` | string or null |  |
| `city` | string or null |  |
| `state` | string or null |  |
| `zip_code` | string or null |  |
