Skip to content

Create a customer

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

POST/api/v1/customers

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):

FieldTypeRequiredDescription
emailstringYesThe customer's email address. One active customer per address in a brand and mode.
namestringNoThe person's name.
businessNamestringNoThe business's legal name, for business customers.
typeone of individual, business, unknownNoWhat 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).
localestringNoLanguage for checkout, the portal, emails and invoices, e.g. de.
billingAddressobjectNo
billingAddress.line1stringNo
billingAddress.line2stringNo
billingAddress.citystringNo
billingAddress.postalCodestringNo
billingAddress.regionstringNo
billingAddress.countrystringYesISO 3166-1 alpha-2 country code (required).
taxIdsarray of objectNoThe 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[].typestringYesThe 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[].valuestringYesThe number as the customer gave it, with or without the country prefix and separators.
taxIds[].countrystringYesThe country that issued the tax ID (ISO 3166-1 alpha-2; EL = Greece).
testClockIdclk_… IDNoTest mode: a test clock (clk_…) to attach the customer to. Its subscriptions bill at the clock's time (spec §16.13).
metadataobjectNoYour own key-value pairs (e.g. your user ID).

Response

201 Created with a JSON object:

FieldTypeDescription
idcus_… ID
object"customer"
livemodeboolean
entityIdent_… ID
brandIdbrd_… ID
emailstring
namestring or null
businessNamestring or null
localestring or null
typeone of individual, business, unknownInferred (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.
declaredTypeone of individual, business, unknownWhat you declared.
billingAddressobject or null
taxIdsarray of object
taxIds[].idstringIdentifies the tax ID on the customer: its canonical number, e.g. ATU12345678.
taxIds[].typestringeu_vat, gb_vat, ch_uid/ch_vat, no_org/no_vat, us_ein, or another type (unverified).
taxIds[].valuestringThe number as given.
taxIds[].countrystring
taxIds[].numberstring or nullThe canonical number (null for types Ownrecord can't check).
taxIds[].verificationobjectThe latest check (spec §9.4).
taxIds[].verification.statusone of valid, invalid, unavailable, format_only, unverifiedvalid, invalid, unavailable (the register couldn't answer), format_only (no register: format and checksum) or unverified (a type Ownrecord can't check).
taxIds[].verification.sourcestringWho decided: VIES, HMRC, CH_UID, FORMAT (format and checksum only) or NONE.
taxIds[].verification.reasonstring or nullWhy, e.g. not_registered or service_unavailable.
taxIds[].verification.checkedAtdate-time
taxIds[].verification.consultationNumberstring or nullThe register's proof of the check (VIES request identifier, HMRC consultation number).
taxIds[].verification.simulatedbooleanA fixture answer (development and test environments), not a register's.
taxIds[].historyarray of objectEarlier checks, newest first (at most 10).
taxIds[].history[].statusone of valid, invalid, unavailable, format_only, unverifiedvalid, invalid, unavailable (the register couldn't answer), format_only (no register: format and checksum) or unverified (a type Ownrecord can't check).
taxIds[].history[].sourcestringWho decided: VIES, HMRC, CH_UID, FORMAT (format and checksum only) or NONE.
taxIds[].history[].reasonstring or nullWhy, e.g. not_registered or service_unavailable.
taxIds[].history[].checkedAtdate-time
taxIds[].history[].consultationNumberstring or nullThe register's proof of the check (VIES request identifier, HMRC consultation number).
taxIds[].history[].simulatedbooleanA fixture answer (development and test environments), not a register's.
taxIds[].addedAtdate-time
taxCountryLockedobject or nullWhere 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.
taxExemptobject or nullTax exemption (spec §9.5). Not applied yet: exemption certificates come in a later release.
processorRefsarray of objectThe customer at each payment processor used (set by checkout).
processorRefs[].processorConnectionIdpcon_… ID
processorRefs[].customerRefstring
riskobject
risk.levelone of normal, elevated, blocked
risk.reasonsarray of string
risk.firstPaymentAtdate-time or null
risk.totalPaidmoney
portalLastLoginAtdate-time or null
testClockIdclk_… ID or nullTest mode: the test clock the customer was created on (spec §16.13).
metadataobject
createdAtdate-time
updatedAtdate-time
deletedAtdate-time or nullWhen 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

CodeHTTP statusWhenWhat to do
validation_failed400A 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_invalid400A 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_taken409Another 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_found404No 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_mode400testClockId 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_advancing409The 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_limit400The test clock already has 10 customers.Create another test clock for more customers. A clock holds at most 10 customers.
idempotency_key_reused409The idempotency key was used before with another input.Use a new Idempotency-Key for a different request, or resend the original request unchanged.
scope_required400The 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_allowed403You 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_denied403You 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: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_required, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.