Skip to content

Update a customer

The customers.update operation: PATCH /api/v1/customers/{id} in the REST API and the customers_update tool in the MCP server.

PATCH/api/v1/customers/{id}

Changes a customer's email, name, business name, declared type, language, billing address or metadata. Send only the fields to change; null removes an optional one. billingAddress and metadata are replaced as a whole.

The type is inferred again (spec §13.1). Tax IDs have their own operations (customers.tax_ids.add, customers.tax_ids.delete), and the locked tax country changes only through payments (spec §9.3). A changed billing address is used as evidence at the next checkout; it doesn't change where past sales were taxed.

Emits customer.updated with the old values in previous_attributes (nothing when nothing changed).

At a glance

  • REST: PATCH /api/v1/customers/{id}
  • MCP tool: customers_update
  • Operation ID: customers.update
  • Scope: Brand
  • Permission: customers: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 customers:write scope
  • Since: M4

Request

Path parameters

FieldTypeDescription
idcus_… IDThe customer's ID (cus_…).

Body

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

FieldTypeRequiredDescription
emailstringNo
namestring or nullNonull removes it.
businessNamestring or nullNonull removes it.
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).
localestring or nullNonull removes it.
billingAddressobject or nullNoReplaces the address; null removes it.
metadataobjectNoReplaces the metadata (send {} to clear it).

Response

200 OK 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

Set the language and the full address

HTTP
PATCH /api/v1/customers/cus_01m4ty3c2b5qx7mhd9k8w6s0ab
Authorization: Bearer or_test_…
Content-Type: application/json

{
  "locale": "de-DE",
  "billingAddress": {
    "line1": "Torstraße 5",
    "city": "Berlin",
    "postalCode": "10119",
    "country": "DE"
  }
}

Response (200):

JSON
{
  "id": "cus_01m4ty3c2b5qx7mhd9k8w6s0ab",
  "object": "customer",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "email": "ana@example.com",
  "name": "Ana Schmidt",
  "businessName": null,
  "locale": "de-DE",
  "type": "individual",
  "declaredType": "individual",
  "billingAddress": {
    "line1": "Torstraße 5",
    "line2": null,
    "city": "Berlin",
    "postalCode": "10119",
    "region": null,
    "country": "DE"
  },
  "taxIds": [],
  "taxCountryLocked": {
    "country": "DE",
    "subdivision": null,
    "lockedAt": "2026-10-14T09:30:00.000Z",
    "evidenceSetId": "evs_01m4ty3c2b5qx7mhd9k8w6s0ac"
  },
  "taxExempt": null,
  "processorRefs": [
    {
      "processorConnectionId": "pcon_01m4ty3c2b5qx7mhd9k8w6s0af",
      "customerRef": "cus_QxY12AbC"
    }
  ],
  "risk": {
    "level": "normal",
    "reasons": [],
    "firstPaymentAt": "2026-10-14T09:30:00.000Z",
    "totalPaid": {
      "amount": "119000000",
      "currency": "EUR"
    }
  },
  "portalLastLoginAt": null,
  "testClockId": null,
  "metadata": {},
  "createdAt": "2026-10-14T09:00:00.000Z",
  "updatedAt": "2026-10-14T09:30:00.000Z",
  "deletedAt": null
}

Errors

CodeHTTP statusWhenWhat to do
validation_failed400Nothing to change, a field is malformed, or the customer would be a declared business without a business name or a tax ID.Fix the fields listed in details.issues and send the request again.
resource_not_found404This brand has no such customer in this mode (customers belong to one brand and one mode).Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
customer_deleted400The customer was deleted.Create a new customer for this buyer. Deleted customers stay readable for the records they belong to, but can't be changed.
customer_email_taken409Another active customer of the brand has the new 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.
version_conflict409The customer changed at the same moment; retry.Fetch the object again and retry the change.
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_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.