# Notes

> Read the notes on a customer or a job, and add a note.

- Canonical URL: https://couplrfsm.com/developers/api-reference/notes
- Markdown: https://couplrfsm.com/developers/api-reference/notes.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:** Every customer and every job has a notes log: the gate code, what the customer asked for, what the technician found. You can read it and add to it. What you add shows up in Couplr under the name of whoever made your API key.

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 a customer's notes

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

The notes written on the customer, newest first.

**Path parameters**

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

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

**Example request**

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

**Example response** (`200`)

```json
{
    "data": [
        {
            "id": 9021,
            "body": "Gate code is 4471. Dog is friendly.",
            "is_private": true,
            "is_pinned": false,
            "author_name": "Sam Whitfield",
            "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. |
| `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 a note to a customer

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

The note is added to the log under the name of the person who
created the API key. A note cannot be edited or removed through the
API.

Send `"is_private": true` for a note that is for the company's own
staff only.

**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 |
| --- | --- | --- | --- |
| `body` | string | Yes |  |
| `is_private` | boolean | No | For the company's own staff only. Ignored on a job, where every note is. Default: `false`. |

**Example request**

```bash
curl -X POST "https://couplrfsm.com/api/v1/customers/1842/notes" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_KEY" \
  -d '{"body":"Gate code is 4471. Dog is friendly.","is_private":true}'
```

**Example response** (`201`)

```json
{
    "data": {
        "id": 9021,
        "body": "Gate code is 4471. Dog is friendly.",
        "is_private": true,
        "is_pinned": false,
        "author_name": "Sam Whitfield",
        "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. |

## List a job's notes

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

The notes written on the job, newest first.

**Path parameters**

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

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

**Example request**

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

**Example response** (`200`)

```json
{
    "data": [
        {
            "id": 9021,
            "body": "Gate code is 4471. Dog is friendly.",
            "is_private": true,
            "is_pinned": false,
            "author_name": "Sam Whitfield",
            "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. |
| `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 a note to a job

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

The note is added to the log under the name of the person who
created the API key. A note cannot be edited or removed through the
API.

A note on a job is always internal to the company, whatever
`is_private` says. It does not change the job's `notes` field.

**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 |
| --- | --- | --- | --- |
| `body` | string | Yes |  |
| `is_private` | boolean | No | For the company's own staff only. Ignored on a job, where every note is. Default: `false`. |

**Example request**

```bash
curl -X POST "https://couplrfsm.com/api/v1/jobs/1842/notes" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_KEY" \
  -d '{"body":"Customer asked for a call 30 minutes ahead."}'
```

**Example response** (`201`)

```json
{
    "data": {
        "id": 9021,
        "body": "Gate code is 4471. Dog is friendly.",
        "is_private": true,
        "is_pinned": false,
        "author_name": "Sam Whitfield",
        "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. |
