Update a customer
Updates a customer record. Only fields present in the body are changed.
What can be updated
- Identity -
first_name,last_name,email,phone,language,external_id - Custom fields (
custom.*) - validated against the card’sform_builderschema - Metadata (
metadata.*) - free-form key/value bag
What is READ-ONLY
Balances (stamps, points, tier, gift card balance) are computed from the transaction ledger and cannot be patched. To adjust a balance, create a transaction with action set to award or redeem and value through POST /v1/transactions. A balance cannot be written directly.
Notification
Every successful PATCH bumps passVersion and the pass content timestamp, not only the fields that are visible on the pass. Patching external_id or metadata alone still bumps them, which is harmless but means the version is not a reliable signal that anything the customer can see has changed.
No push is sent from here. The pass is NOT eagerly regenerated and no Apple silent push is fired on this endpoint: that path is certificate-gated and can report a false 404 even after a successful save. Google Wallet reflects the change on its next server-side object sync. So treat a 200 as “stored”, not as “delivered to the device”.
Authorizations
Your secret API key as a Bearer token: Authorization: Bearer vxn_sk_.... Keys are created and scoped in the dashboard - see Authentication.
Headers
Body
Sparse update. card_id is immutable.
Update the customer's external reference (CRM id, internal account number, etc.). Send null to clear it. This is the same field POST /v1/customers uses as the highest-priority dedupe key on creation, but PATCH here only updates the stored value - it never re-triggers dedupe.
"crm-customer-7421"
Contact phone. Accepted in any common written form - +421908123456, +421 908 123 456, +421-908-123-456, 00421908123456, or a bare Slovak local number like 0908123456. The platform canonicalises it to E.164 on write, and every read returns strict E.164 (^\\+[1-9]\\d{1,14}$) or null. A value that cannot be resolved without guessing a country code is rejected with 422 invalid_format rather than stored and silently reported as null.
8 - 16^\+?[0-9][0-9\s().\-]{6,24}$"+14155552671"
Toggle the stored marketing-consent record. This changes the stored value only. VEXiON does not apply it when sending notifications: a notification sent through the API is not filtered by this flag or by the customer's marketing switch in the dashboard. To respect opt-outs on API sends, send only to customers with marketing_opt_in true, using explicit customer_ids.
Partner-internal {key: value} map (max 50 keys, key ^[a-zA-Z0-9_]{1,40}$, string values ≤500 chars). NEVER rendered to the customer - for CRM ids, import batches, integration markers. Customer-facing typed data belongs in custom (validated against custom field definitions, renderable on passes/notifications).
Update semantics (JSON Merge Patch): PATCH merges per key - send only the keys you change; "key": null deletes that key; "metadata": null clears the whole map; omitting metadata leaves it untouched.
Response
Updated customer.
Customer identifier - an opaque, server-issued token prefixed cus_. Encodes the internal record through a keyed cipher (AES-128 + an integrity tag, base32-encoded) - not decodable or guessable client-side; the prefix lets clients route on resource type without parsing the ID.
^cus_[A-Za-z0-9]+$"cus_g2y5z2ylfkrybsthe2qnweualy"
A record of the customer's marketing consent, stored and returned for your own use and audits. VEXiON does not apply it when sending notifications: a notification sent through the API is not filtered by this flag or by the customer's marketing switch in the dashboard. To respect opt-outs on API sends, send only to customers with marketing_opt_in true, using explicit customer_ids.
Wallet pass install state on the customer's device. installed = pass on at least one device, not_installed = signed up but pass not yet added, deleted = customer removed the pass, disputed = wallet flagged the pass. A customer with no stored status reads not_installed; the value is never null.
installed, not_installed, deleted, disputed, null Save-to-Google-Wallet URL for this customer's card, or null if not yet generated. Generated lazily: null on a freshly created customer, and populated on the first GET /v1/customers/{id} for that customer (not on create, and not on the list endpoint) - persisted after the first successful generation, so later reads return the same URL without regenerating it. A transient generation failure leaves this null; retry by reading the customer again.
The value encoded in the customer's wallet pass QR code or barcode, and the number printed on physical cards. A merchant-assigned custom number when serial_number_locked is true; otherwise a platform-assigned 24-character hex value, or a referral link ending in it when the card has referrals enabled. NOT the customer id: to find the customer behind a scanned code, call GET /v1/customers?serial_number=<scanned value>.
Custom field VALUES for this customer, keyed by the definition's key. Validated against the card's form and the cf_ definitions - unknown keys are rejected (422 unknown_custom_field).
Value shapes by field type: text/textarea → string; number → number (integer when the definition says so); currency → integer minor units; date → YYYY / YYYY-MM / YYYY-MM-DD per granularity; boolean → true/false; select → one option value; multiselect → array of option values.
Renderable everywhere via {{custom.<key>}}: pass slots, notification title/body - typed values format in the customer's locale.
Partner-internal {key: value} map (max 50 keys, key ^[a-zA-Z0-9_]{1,40}$, string values ≤500 chars). NEVER rendered to the customer - for CRM ids, import batches, integration markers. Customer-facing typed data belongs in custom (validated against custom field definitions, renderable on passes/notifications).
Update semantics (JSON Merge Patch): PATCH merges per key - send only the keys you change; "key": null deletes that key; "metadata": null clears the whole map; omitting metadata leaves it untouched.
Unix seconds of the customer's most recent confirmed transaction - ANY channel (POS, scanner, API, online). Stored denormalized for fast filtering; equals the newest ledger entry (see GET /v1/transactions), except that gift card lock, employee refill and merchant issue rows do not move it.
1782604800
Unix seconds until which the customer keeps the current discount or reward tier (level retention, mechanics.tier_demotion). It is the end of the Bratislava calendar day: a qualifying transaction on or before that day keeps the level, and the first daily run after it moves the customer down one tier. Null when level retention is off on the card, the customer is on the lowest tier, the card cannot use level retention (employee discount, Booqme or Bookio card, flat reward, fewer than 2 tiers), or the card entry is deleted or disputed. Reminders go out 14 and 3 days before this date.
1807833599
Location of the most recent confirmed transaction - ANY channel. Stored denormalized for fast filtering; equals the newest ledger entry (see GET /v1/transactions), except that gift card lock, employee refill and merchant issue rows do not move it.
"loc_g2f2cbt7cbuz3hambazorvws2q"