# Create a customer

> The customers.create operation: POST /api/v1/customers in the REST API and the customers_create tool in the MCP server.

Creates a customer of the brand in the mode of the request (spec §7.5). Checkout creates customers itself; use
this to create them ahead of a sale or from your own sign-up.

- **Email:** one active customer per address in a brand and mode (`customer_email_taken` otherwise). The same
  address in another brand is another customer.
- **Tax IDs:** each is checked with its register before it is stored (VIES for EU VAT IDs, HMRC for UK VAT
  numbers, the Swiss UID register; Norwegian and US numbers by format; other types are kept `unverified`).
  The result, its source, the consultation number and the time are stored per ID. A malformed number is
  refused (`tax_id_invalid`); a number the register doesn't know is stored as `invalid`. Dry runs simulate
  the check.
- **Type:** inferred (spec §13.1): `business` with a tax ID that isn't `invalid`, or with `businessName` when you
  declare `type: business`; otherwise what you declared. Tax is still decided per sale: only a `valid` tax ID
  makes a sale business-to-business, and the location comes from the evidence checkout captures.

Emits `customer.created`.

## At a glance

- **REST:** `POST /api/v1/customers`
- **MCP tool:** `customers_create`
- **Operation ID:** `customers.create`
- **Scope:** Brand
- **Permission:** `customers:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `customers:write` scope
- **Since:** M4

## Request

### Body

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

| Field                       | Type                                       | Required | Description                                                                                                                                                                                                       |
| --------------------------- | ------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email`                     | string                                     | Yes      | The customer's email address. One active customer per address in a brand and mode.                                                                                                                                |
| `name`                      | string                                     | No       | The person's name.                                                                                                                                                                                                |
| `businessName`              | string                                     | No       | The business's legal name, for business customers.                                                                                                                                                                |
| `type`                      | one of `individual`, `business`, `unknown` | No       | What you say the customer is: `individual`, `business` (needs `businessName` or a tax ID) or `unknown`. The stored `type` is inferred: a tax ID that isn't rejected makes the customer a `business` (spec §13.1). |
| `locale`                    | string                                     | No       | Language for checkout, the portal, emails and invoices, e.g. `de`.                                                                                                                                                |
| `billingAddress`            | object                                     | No       |                                                                                                                                                                                                                   |
| `billingAddress.line1`      | string                                     | No       |                                                                                                                                                                                                                   |
| `billingAddress.line2`      | string                                     | No       |                                                                                                                                                                                                                   |
| `billingAddress.city`       | string                                     | No       |                                                                                                                                                                                                                   |
| `billingAddress.postalCode` | string                                     | No       |                                                                                                                                                                                                                   |
| `billingAddress.region`     | string                                     | No       |                                                                                                                                                                                                                   |
| `billingAddress.country`    | string                                     | Yes      | ISO 3166-1 alpha-2 country code (required).                                                                                                                                                                       |
| `taxIds`                    | array of object                            | No       | The customer's tax IDs. Each is checked with its register (VIES, HMRC, the Swiss UID register) and stored with the result; a malformed number is refused (`tax_id_invalid`).                                      |
| `taxIds[].type`             | string                                     | Yes      | The tax ID's type: `eu_vat`, `gb_vat`, `ch_uid`/`ch_vat`, `no_org`/`no_vat` or `us_ein`. Other types are kept but can't be checked (`unverified`).                                                                |
| `taxIds[].value`            | string                                     | Yes      | The number as the customer gave it, with or without the country prefix and separators.                                                                                                                            |
| `taxIds[].country`          | string                                     | Yes      | The country that issued the tax ID (ISO 3166-1 alpha-2; `EL` = Greece).                                                                                                                                           |
| `testClockId`               | `clk_…` ID                                 | No       | Test mode: a test clock (`clk_…`) to attach the customer to. Its subscriptions bill at the clock's time (spec §16.13).                                                                                            |
| `metadata`                  | object                                     | No       | Your own key-value pairs (e.g. your user ID).                                                                                                                                                                     |

## Response

`201 Created` with a JSON object:

| Field                                      | Type                                                                  | Description                                                                                                                                                                                              |
| ------------------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                       | `cus_…` ID                                                            |                                                                                                                                                                                                          |
| `object`                                   | `"customer"`                                                          |                                                                                                                                                                                                          |
| `livemode`                                 | boolean                                                               |                                                                                                                                                                                                          |
| `entityId`                                 | `ent_…` ID                                                            |                                                                                                                                                                                                          |
| `brandId`                                  | `brd_…` ID                                                            |                                                                                                                                                                                                          |
| `email`                                    | string                                                                |                                                                                                                                                                                                          |
| `name`                                     | string or null                                                        |                                                                                                                                                                                                          |
| `businessName`                             | string or null                                                        |                                                                                                                                                                                                          |
| `locale`                                   | string or null                                                        |                                                                                                                                                                                                          |
| `type`                                     | one of `individual`, `business`, `unknown`                            | Inferred (spec §13.1): `business` with a tax ID that isn't rejected, or with a business name when declared a business. Tax is decided per sale: only a `valid` tax ID makes a sale business-to-business. |
| `declaredType`                             | one of `individual`, `business`, `unknown`                            | What you declared.                                                                                                                                                                                       |
| `billingAddress`                           | object or null                                                        |                                                                                                                                                                                                          |
| `taxIds`                                   | array of object                                                       |                                                                                                                                                                                                          |
| `taxIds[].id`                              | string                                                                | Identifies the tax ID on the customer: its canonical number, e.g. `ATU12345678`.                                                                                                                         |
| `taxIds[].type`                            | string                                                                | `eu_vat`, `gb_vat`, `ch_uid`/`ch_vat`, `no_org`/`no_vat`, `us_ein`, or another type (unverified).                                                                                                        |
| `taxIds[].value`                           | string                                                                | The number as given.                                                                                                                                                                                     |
| `taxIds[].country`                         | string                                                                |                                                                                                                                                                                                          |
| `taxIds[].number`                          | string or null                                                        | The canonical number (null for types Ownrecord can't check).                                                                                                                                             |
| `taxIds[].verification`                    | object                                                                | The latest check (spec §9.4).                                                                                                                                                                            |
| `taxIds[].verification.status`             | one of `valid`, `invalid`, `unavailable`, `format_only`, `unverified` | `valid`, `invalid`, `unavailable` (the register couldn't answer), `format_only` (no register: format and checksum) or `unverified` (a type Ownrecord can't check).                                       |
| `taxIds[].verification.source`             | string                                                                | Who decided: VIES, HMRC, CH_UID, FORMAT (format and checksum only) or NONE.                                                                                                                              |
| `taxIds[].verification.reason`             | string or null                                                        | Why, e.g. `not_registered` or `service_unavailable`.                                                                                                                                                     |
| `taxIds[].verification.checkedAt`          | date-time                                                             |                                                                                                                                                                                                          |
| `taxIds[].verification.consultationNumber` | string or null                                                        | The register's proof of the check (VIES request identifier, HMRC consultation number).                                                                                                                   |
| `taxIds[].verification.simulated`          | boolean                                                               | A fixture answer (development and test environments), not a register's.                                                                                                                                  |
| `taxIds[].history`                         | array of object                                                       | Earlier checks, newest first (at most 10).                                                                                                                                                               |
| `taxIds[].history[].status`                | one of `valid`, `invalid`, `unavailable`, `format_only`, `unverified` | `valid`, `invalid`, `unavailable` (the register couldn't answer), `format_only` (no register: format and checksum) or `unverified` (a type Ownrecord can't check).                                       |
| `taxIds[].history[].source`                | string                                                                | Who decided: VIES, HMRC, CH_UID, FORMAT (format and checksum only) or NONE.                                                                                                                              |
| `taxIds[].history[].reason`                | string or null                                                        | Why, e.g. `not_registered` or `service_unavailable`.                                                                                                                                                     |
| `taxIds[].history[].checkedAt`             | date-time                                                             |                                                                                                                                                                                                          |
| `taxIds[].history[].consultationNumber`    | string or null                                                        | The register's proof of the check (VIES request identifier, HMRC consultation number).                                                                                                                   |
| `taxIds[].history[].simulated`             | boolean                                                               | A fixture answer (development and test environments), not a register's.                                                                                                                                  |
| `taxIds[].addedAt`                         | date-time                                                             |                                                                                                                                                                                                          |
| `taxCountryLocked`                         | object or null                                                        | Where the customer's purchases are taxed (spec §9.3): set at the first successful payment, moved only when a top-up's evidence resolves to another country.                                              |
| `taxExempt`                                | object or null                                                        | Tax exemption (spec §9.5). Not applied yet: exemption certificates come in a later release.                                                                                                              |
| `processorRefs`                            | array of object                                                       | The customer at each payment processor used (set by checkout).                                                                                                                                           |
| `processorRefs[].processorConnectionId`    | `pcon_…` ID                                                           |                                                                                                                                                                                                          |
| `processorRefs[].customerRef`              | string                                                                |                                                                                                                                                                                                          |
| `risk`                                     | object                                                                |                                                                                                                                                                                                          |
| `risk.level`                               | one of `normal`, `elevated`, `blocked`                                |                                                                                                                                                                                                          |
| `risk.reasons`                             | array of string                                                       |                                                                                                                                                                                                          |
| `risk.firstPaymentAt`                      | date-time or null                                                     |                                                                                                                                                                                                          |
| `risk.totalPaid`                           | money                                                                 |                                                                                                                                                                                                          |
| `portalLastLoginAt`                        | date-time or null                                                     |                                                                                                                                                                                                          |
| `testClockId`                              | `clk_…` ID or null                                                    | Test mode: the test clock the customer was created on (spec §16.13).                                                                                                                                     |
| `metadata`                                 | object                                                                |                                                                                                                                                                                                          |
| `createdAt`                                | date-time                                                             |                                                                                                                                                                                                          |
| `updatedAt`                                | date-time                                                             |                                                                                                                                                                                                          |
| `deletedAt`                                | date-time or null                                                     | When the customer was deleted (soft delete); null while active.                                                                                                                                          |

## Examples

### A business in Austria with its VAT ID

VIES confirms the VAT ID, so the customer is a business.

```http
POST /api/v1/customers
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "email": "billing@alpenapps.example",
  "name": "Lena Huber",
  "businessName": "Alpen Apps GmbH",
  "type": "business",
  "locale": "de-AT",
  "billingAddress": {
    "line1": "Mariahilfer Straße 1",
    "city": "Wien",
    "postalCode": "1060",
    "country": "AT"
  },
  "taxIds": [
    {
      "type": "eu_vat",
      "value": "ATU 0000 0000",
      "country": "AT"
    }
  ],
  "metadata": {
    "userId": "u_4821"
  }
}
```

Response (`201`):

```json
{
  "id": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
  "object": "customer",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "email": "billing@alpenapps.example",
  "name": "Lena Huber",
  "businessName": "Alpen Apps GmbH",
  "locale": "de-AT",
  "type": "business",
  "declaredType": "business",
  "billingAddress": {
    "line1": "Mariahilfer Straße 1",
    "line2": null,
    "city": "Wien",
    "postalCode": "1060",
    "region": null,
    "country": "AT"
  },
  "taxIds": [
    {
      "id": "ATU00000000",
      "type": "eu_vat",
      "value": "ATU 0000 0000",
      "country": "AT",
      "number": "ATU00000000",
      "verification": {
        "status": "valid",
        "source": "VIES",
        "reason": null,
        "checkedAt": "2026-10-14T09:00:00.000Z",
        "consultationNumber": "WAPIAAAAZ1x2y3",
        "simulated": false
      },
      "history": [],
      "addedAt": "2026-10-14T09:00:00.000Z"
    }
  ],
  "taxCountryLocked": null,
  "taxExempt": null,
  "processorRefs": [],
  "risk": {
    "level": "normal",
    "reasons": [],
    "firstPaymentAt": null,
    "totalPaid": {
      "amount": "0",
      "currency": "EUR"
    }
  },
  "portalLastLoginAt": null,
  "testClockId": null,
  "metadata": {
    "userId": "u_4821"
  },
  "createdAt": "2026-10-14T09:00:00.000Z",
  "updatedAt": "2026-10-14T09:00:00.000Z",
  "deletedAt": null
}
```

### A consumer

```http
POST /api/v1/customers
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "email": "ana@example.com",
  "name": "Ana Schmidt",
  "type": "individual",
  "billingAddress": {
    "country": "DE"
  }
}
```

Response (`201`):

```json
{
  "id": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
  "object": "customer",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "email": "ana@example.com",
  "name": "Ana Schmidt",
  "businessName": null,
  "locale": null,
  "type": "individual",
  "declaredType": "individual",
  "billingAddress": {
    "line1": null,
    "line2": null,
    "city": null,
    "postalCode": null,
    "region": null,
    "country": "DE"
  },
  "taxIds": [],
  "taxCountryLocked": null,
  "taxExempt": null,
  "processorRefs": [],
  "risk": {
    "level": "normal",
    "reasons": [],
    "firstPaymentAt": null,
    "totalPaid": {
      "amount": "0",
      "currency": "EUR"
    }
  },
  "portalLastLoginAt": null,
  "testClockId": null,
  "metadata": {},
  "createdAt": "2026-10-14T09:00:00.000Z",
  "updatedAt": "2026-10-14T09:00:00.000Z",
  "deletedAt": null
}
```

## Errors

| Code                                                                                                   | HTTP status | When                                                                                                                                                                                                                                                               | What to do                                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                 | 400         | A field is missing or malformed, a tax ID is given twice, or a business customer has neither a business name nor a tax ID (`details.issues`).                                                                                                                      | Fix the fields listed in `details.issues` and send the request again.                                                                                                                               |
| [`tax_id_invalid`](https://ownrecord.io/docs/reference/errors.md#tax_id_invalid)                       | 400         | A tax ID's number isn't valid for its type and country (format, checksum, another country's prefix, a Northern Ireland XI number); `details.reason` says which. Nothing is stored. Types Ownrecord can't check (e.g. `au_abn`) are stored as `unverified` instead. | Check the number and its country for typing errors (`details.reason` says what is wrong), then send it again. A Northern Ireland business enters its GB VAT number.                                 |
| [`customer_email_taken`](https://ownrecord.io/docs/reference/errors.md#customer_email_taken)           | 409         | Another active customer of the brand has this email address in this mode.                                                                                                                                                                                          | Use the existing customer (`details.customer_id`, or `customers.list` with `email`), or give this customer another address. A deleted customer's address can be used again.                         |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)               | 404         | No test clock with `testClockId` exists for the brand in test mode.                                                                                                                                                                                                | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                                                |
| [`test_clock_mode`](https://ownrecord.io/docs/reference/errors.md#test_clock_mode)                     | 400         | `testClockId` was given in live mode: test clocks belong to test mode.                                                                                                                                                                                             | Attach test clocks to test-mode customers only. Live customers bill at the real time.                                                                                                               |
| [`test_clock_advancing`](https://ownrecord.io/docs/reference/errors.md#test_clock_advancing)           | 409         | The test clock is advancing: wait until it is ready.                                                                                                                                                                                                               | Wait until the clock's `status` is `ready` (read it with `test_clocks.get`), then try again. An advance processes every due renewal, trial end and cancellation of the clock's subscriptions first. |
| [`test_clock_customer_limit`](https://ownrecord.io/docs/reference/errors.md#test_clock_customer_limit) | 400         | The test clock already has 10 customers.                                                                                                                                                                                                                           | Create another test clock for more customers. A clock holds at most 10 customers.                                                                                                                   |
| [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused)       | 409         | The idempotency key was used before with another input.                                                                                                                                                                                                            | Use a new Idempotency-Key for a different request, or resend the original request unchanged.                                                                                                        |
| [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required)                       | 400         | The request names no brand (an org or entity key without `Ownrecord-Brand`).                                                                                                                                                                                       | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (`details.required_scope`).                                                  |
| [`livemode_not_allowed`](https://ownrecord.io/docs/reference/errors.md#livemode_not_allowed)           | 403         | You have no live-mode access, so you can't act on live customers.                                                                                                                                                                                                  | Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin.                                                                         |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                 | 403         | You can't change this brand's customers (`customers:write`: owners, admins and support).                                                                                                                                                                           | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                                     |

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_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
