# Update your email preferences

> The email_preferences.update operation: PATCH /api/v1/email_preferences in the REST API and the email_preferences_update tool in the MCP server.

Turns topics of Ownrecord's own emails on or off for your account: `onboarding_nudges`, `usage_summary`,
`product_updates` (marketing: off until you opt in here) and `subprocessor_changes`. Account emails can't be
turned off. Each change is recorded with its time and where it was made.

Any signed-in user, for their own account. The link at the bottom of Ownrecord's emails changes the
preferences of the user it was sent to, without a sign-in.

## At a glance

- **REST:** `PATCH /api/v1/email_preferences`
- **MCP tool:** `email_preferences_update`
- **Operation ID:** `email_preferences.update`
- **Scope:** Platform
- **Permission:** `account:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` optional
- **Modes:** Test and live
- **Authentication:** API key with the `account:write` scope
- **Since:** M12

## Request

### Body

A JSON object (`Content-Type: application/json`):

| Field                         | Type                                                     | Required | Description |
| ----------------------------- | -------------------------------------------------------- | -------- | ----------- |
| `token`                       | string                                                   | No       |             |
| `topics`                      | object                                                   | Yes      |             |
| `topics.onboarding_nudges`    | boolean                                                  | No       |             |
| `topics.usage_summary`        | boolean                                                  | No       |             |
| `topics.product_updates`      | boolean                                                  | No       |             |
| `topics.subprocessor_changes` | boolean                                                  | No       |             |
| `source`                      | one of `preferences_page`, `subprocessor_page`, `signup` | No       |             |

## Response

`200 OK` with a JSON object:

| Field                  | Type                                                                                   | Description |
| ---------------------- | -------------------------------------------------------------------------------------- | ----------- |
| `object`               | `"email_preferences"`                                                                  |             |
| `userId`               | `usr_…` ID                                                                             |             |
| `topics`               | array of object                                                                        |             |
| `topics[].topic`       | one of `onboarding_nudges`, `usage_summary`, `product_updates`, `subprocessor_changes` |             |
| `topics[].label`       | string                                                                                 |             |
| `topics[].description` | string                                                                                 |             |
| `topics[].consent`     | one of `service`, `marketing`                                                          |             |
| `topics[].enabled`     | boolean                                                                                |             |
| `topics[].changedAt`   | date-time or null                                                                      |             |
| `updatedAt`            | date-time or null                                                                      |             |

## Examples

### Opt in to product updates

```http
PATCH /api/v1/email_preferences
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "topics": {
    "product_updates": true
  }
}
```

Response (`200`):

```json
{
  "object": "email_preferences",
  "userId": "usr_01m4tx5nj8emwbyf7y9vcf47xa",
  "topics": [
    {
      "topic": "onboarding_nudges",
      "label": "Onboarding reminders",
      "description": "A reminder 2 and 7 days after you create an organization, while setup steps are still open.",
      "consent": "service",
      "enabled": true,
      "changedAt": null
    },
    {
      "topic": "usage_summary",
      "label": "Monthly usage summary",
      "description": "Your live volume, Ownrecord fees and an estimate of what a platform merchant of record would cost.",
      "consent": "service",
      "enabled": true,
      "changedAt": null
    },
    {
      "topic": "product_updates",
      "label": "Product updates",
      "description": "A digest of new features from the changelog. Only if you opt in.",
      "consent": "marketing",
      "enabled": true,
      "changedAt": "2026-10-27T09:00:00.000Z"
    },
    {
      "topic": "subprocessor_changes",
      "label": "Sub-processor changes",
      "description": "Notice of a new or replaced sub-processor, 30 days ahead. Owners and admins of organizations that accepted the DPA always get it.",
      "consent": "service",
      "enabled": false,
      "changedAt": null
    }
  ],
  "updatedAt": "2026-10-27T09:00:00.000Z"
}
```

## Errors

| Code                                                                                     | HTTP status | When                                                                                      | What to do                                                                                           |
| ---------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)   | 400         | No topic was given, a topic isn't one of the four, or a signed-in user sent a link token. | Fix the fields listed in `details.issues` and send the request again.                                |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)   | 403         | The caller isn't a signed-in person, or the email-preferences link isn't valid.           | Ask an organization owner or admin for a role or scope that allows this action.                      |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found) | 404         | The user doesn't exist.                                                                   | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
| [`version_conflict`](https://ownrecord.io/docs/reference/errors.md#version_conflict)     | 409         | The preferences changed at the same time; retry.                                          | Fetch the object again and retry the change.                                                         |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
