Skip to main content
PATCH

Authorizations

Authorization
string
header
required

Your secret API key as a Bearer token: Authorization: Bearer vxn_sk_.... Keys are created and scoped in the dashboard - see Authentication.

Headers

Idempotency-Key
string

Recommended on PATCH and DELETE for safe retries.

Maximum string length: 255
Accept-Language
enum<string>
default:sk

Preferred language for error messages and localized fields. One of sk, cs, en, de. Defaults to card's default_language.

Available options:
sk,
cs,
en,
de

Path Parameters

id
string
required

Card template identifier. Opaque, server-issued token, e.g. crd_c4zdlocdwajf6wtmnvvkonv4oa.

Pattern: ^crd_[A-Za-z0-9]+$

Body

application/json

Body for PATCH /v1/cards/{id}. All fields optional - send only what you want to change. NOTE: type and id are immutable and cannot be patched. Mechanics block, if provided, must match the existing card type. Patching design, mechanics, or pass_fields triggers a silent push to all installed wallet passes within 2 seconds. A mechanics block must repeat the required keys of its type (reward: type, earn_mode, earn_rate_per_unit, redemption_mode; discount: type, tiers; coupon: type, is_reusable; stamp: type, stamps_required). Every other mechanics key is optional and keeps its stored value. mechanics for gift_card and membership cards returns 422 card_type_not_writable.

name

Update the display name - plain string or per-language object ({sk, cs, en, de}).

Required string length: 1 - 100
description

Update the description (string OR per-language object).

external_id
string | null

Update the partner reference. Persisted on the card template and returned on reads (null when unset). Not unique: two templates may share a reference, so GET /v1/cards?external_id= filters and can return more than one row. Send null to clear it.

Maximum string length: 255
languages_supported
enum<string>[]

Update the supported language list.

Available options:
sk,
cs,
en,
de
default_language
enum<string>

Update the default language.

Available options:
sk,
cs,
en,
de
barcode_format
enum<string>

Update the barcode format. Existing wallet passes regenerate on next refresh.

Available options:
QR,
Aztec,
PDF417,
Code128,
Code39,
EAN8,
EAN13,
ITF14,
UPCA,
DataMatrix,
Codabar
purchase_amount_required
boolean
default:false

Scanner UX flag. When true, staff MUST enter a purchase amount before they can record a transaction against this card. When false, the amount field is optional (staff can record visit-only / quick-redeem transactions). Affects scanner behavior across all card types.

Example:

false

design
object

Partial update. Visual design of the wallet pass. Required: design_mode, bg_color, text_color, icon_url (logo_url is optional; the pass falls back to text when omitted).

VISUAL LAYOUT by mode + type:

  • design_mode=standard + stamp: mechanics.stamp_icon_url + (optional custom_background_image_url)
  • design_mode=individual + stamp: mechanics.stamp_count_images (N+1 per-position images; image_url accepts a plain URL or an OPTIONAL per-language map - every position must share the same language set; each customer sees the variant in their language, else sk, else en, else the first one)
  • design_mode=standard + non-stamp: pattern_type + pattern_icon_urls + (optional custom_background_image_url) Server AUTO-GENERATES the final pattern image per supported language: pass a per-language object as card description and each language gets its own image; single-language cards (or a plain-string description) get one image. Each customer is served the image matching the language they picked at signup (fallback sk, then en, then the first one). No image URLs are needed for the standard pattern.
  • design_mode=individual + non-stamp: individual_background_image_url REQUIRED (accepts a plain URL or an OPTIONAL per-language map; each customer sees the variant in their language, else sk, else en, else the first one)

TIER OVERRIDES: tiered reward/discount cards use mechanics.tiers[i].background_image_urls (per-language) which overrides any card-level background for customers at that tier.

pass_fields
object

The wallet pass layout - three front slots + back rows, identical model on Apple and Google (the same shape the platform's partner integrations have used in production):

The PRIMARY front area (the big number - stamps, points, balance) is owned by the card's mechanics and never configurable here. Typed values ({{custom.<key>}} of type date/number/currency/boolean) render locale-formatted on both platforms automatically.

PATCH replaces this object as a WHOLE, it does not merge per slot. Any slot you omit is REMOVED from the card. Sending {"pass_fields": {"header": {...}}} on a card that also had front_left, front_right, back or barcode_alt_text clears those. To change one slot, send the full pass_fields object including every slot you want to keep, or read the card first and send it back with your edit applied. This differs from metadata and custom on other resources, which ARE JSON Merge Patch (a per-key merge where null deletes one key).

Example:
form_builder
object

Updated signup form. Same shape as on create (must keep at minimum first_name + email).

mechanics
object

Type-specific mechanics block. Optional on PATCH, but if you send it, it must match Card.type. Pick the matching tab. Custom = pick 'Custom (null)'. An unknown key inside mechanics (also inside a reward, a level or employee) answers 422 parameter_unknown with the full name, for example mechanics.rewards[0].colour. Before, an unknown key was ignored.

issue_limit
integer | null

Update the enrollment cap. Lowering below issued_count blocks NEW enrollments but does not deactivate existing customers. Capped at 100,000,000 to prevent overflow abuse. 0 and null both mean unlimited; both read back as null.

Required range: 0 <= x <= 100000000
referral
object | null

The block merges into the stored referral settings, key by key. A key you leave out keeps its stored value. Send only what changes, for example {"enabled": false}.

issuer
object | null

Issuer details. Merge-patch: send only the keys you want to change.

terms_of_use
object | null

Optional terms and conditions shown on the wallet pass back side. text supports per-language (string OR object {sk, cs, en, de}) - the pass renders the customer's own language variant when present, falling back to the card's default-language text otherwise. full_url is a single URL only (no per-language variant).

metadata
object

Update metadata. Keys not provided are kept. Set a key to null to remove it. The merged result must stay within 50 keys: a sequence of individually valid patches that would exceed the cap is rejected with 422 validation_failed. Send {} to clear all.

Persisted on the card template and returned on reads. Omitted entirely when empty rather than sent as null. On PATCH the map is merged per key, so sending one key never clears the others.

status
enum<string>

Move the card through its lifecycle. Legal moves are draft->active, active->paused|archived, paused->active|archived, and archived->active; anything else is 409 invalid_status_transition. Patching the status a card already has is an idempotent no-op. draft is not accepted here - nothing transitions INTO draft, so a launched card cannot be un-launched. paused closes the card to NEW signups while existing passes keep working and keep earning. Use archived instead of DELETE for explicit archival, and active to un-archive.

Available options:
active,
paused,
archived

Card-attached links shown on the wallet pass back side - business links (website/social) and a review-prompt link. Max 10 items.

Maximum array length: 10
Example:

Response

Updated card.

A card template defines the loyalty program a merchant offers. The template controls visual design, mechanics (stamps/points/discount/etc.), the customer signup form, wallet pass content, and behavioral defaults. Customers are enrolled in exactly one card via POST /v1/customers. Cards are immutable on type after creation.

id
string
required

Card identifier - an opaque, server-issued token prefixed crd_. Immutable.

Pattern: ^crd_[A-Za-z0-9]+$
Example:

"crd_sophcrprtvpmmnwxah2lomnkee"

type
enum<string>
required

Card mechanics type. Immutable after creation. Drives which mechanics block applies, which initial_state overrides are valid on POST /v1/customers, and which wallet pass layout is used. 'custom' is partner-driven and has no built-in mechanics. Notifications reach every card type, including custom.

Available options:
stamp,
reward,
discount,
coupon,
membership,
gift_card,
custom
Example:

"stamp"

name
required

Display name of the card template. Accepts a plain string (used in all languages) OR a per-language object {sk: '...', en: '...'} for localized names.

Required string length: 1 - 100
Example:
description
required

REQUIRED. Brief customer-facing description of the card/loyalty program. Required by Apple PKPass (description field, used for accessibility/voice-over) and Google LoyaltyClass (programName description for review approval). Can be a string OR per-language object for multi-language passes. Min 1, max 500 chars per language.

Required string length: 1 - 500
Example:
languages_supported
enum<string>[]
required

Languages this card template supports. Customer.language must be one of these. Pass content and notification templates must provide values for each.

Minimum array length: 1
Available options:
sk,
cs,
en,
de
Example:
default_language
enum<string>
required

Default language used when the customer's preferred language is not in languages_supported.

Available options:
sk,
cs,
en,
de
Example:

"sk"

design
object
required

Visual design of the wallet pass. Required: design_mode, bg_color, text_color, icon_url (logo_url is optional; the pass falls back to text when omitted).

VISUAL LAYOUT by mode + type:

  • design_mode=standard + stamp: mechanics.stamp_icon_url + (optional custom_background_image_url)
  • design_mode=individual + stamp: mechanics.stamp_count_images (N+1 per-position images; image_url accepts a plain URL or an OPTIONAL per-language map - every position must share the same language set; each customer sees the variant in their language, else sk, else en, else the first one)
  • design_mode=standard + non-stamp: pattern_type + pattern_icon_urls + (optional custom_background_image_url) Server AUTO-GENERATES the final pattern image per supported language: pass a per-language object as card description and each language gets its own image; single-language cards (or a plain-string description) get one image. Each customer is served the image matching the language they picked at signup (fallback sk, then en, then the first one). No image URLs are needed for the standard pattern.
  • design_mode=individual + non-stamp: individual_background_image_url REQUIRED (accepts a plain URL or an OPTIONAL per-language map; each customer sees the variant in their language, else sk, else en, else the first one)

TIER OVERRIDES: tiered reward/discount cards use mechanics.tiers[i].background_image_urls (per-language) which overrides any card-level background for customers at that tier.

mechanics
object
required

Type-specific mechanics block. REQUIRED but must match Card.type. Pick the matching tab. Custom = pick 'Custom (null)'.

issuer
object
required

REQUIRED. Issuer details. company_name is REQUIRED because it maps to Apple PKPass.organizationName (shown on lock screen) and Google LoyaltyClass.issuerName (shown in pass list). Without it, Apple shows 'Unknown Issuer' and Google rejects the pass during review.

object
string

Resource type discriminator. Always 'card' for this resource. Useful for client libraries that handle polymorphic responses (e.g., webhooks delivering mixed event types). Server always sets it.

Allowed value: "card"
Example:

"card"

external_id
string | null

Your stable reference for this card template (e.g., your internal product SKU). Useful for syncing with external systems. Persisted on the card template and returned on reads (null when unset). Not unique: two templates may share a reference, so GET /v1/cards?external_id= filters and can return more than one row. Send null to clear it.

Maximum string length: 255
Example:

"loyalty-coffee-stamp-2026"

barcode_format
enum<string>
default:QR

Barcode format rendered on the wallet pass to identify customers at POS. QR/Aztec/PDF417/Code128 work on both Apple and Google Wallet; others are Google Wallet only.

Available options:
QR,
Aztec,
PDF417,
Code128,
Code39,
EAN8,
EAN13,
ITF14,
UPCA,
DataMatrix,
Codabar
Example:

"QR"

purchase_amount_required
boolean
default:false

Scanner UX flag. When true, staff MUST enter a purchase amount before they can record a transaction against this card. When false, the amount field is optional (staff can record visit-only / quick-redeem transactions). Affects scanner behavior across all card types.

Example:

false

pass_fields
object

The wallet pass layout - three front slots + back rows, identical model on Apple and Google (the same shape the platform's partner integrations have used in production):

The PRIMARY front area (the big number - stamps, points, balance) is owned by the card's mechanics and never configurable here. Typed values ({{custom.<key>}} of type date/number/currency/boolean) render locale-formatted on both platforms automatically.

PATCH replaces this object as a WHOLE, it does not merge per slot. Any slot you omit is REMOVED from the card. Sending {"pass_fields": {"header": {...}}} on a card that also had front_left, front_right, back or barcode_alt_text clears those. To change one slot, send the full pass_fields object including every slot you want to keep, or read the card first and send it back with your edit applied. This differs from metadata and custom on other resources, which ARE JSON Merge Patch (a per-key merge where null deletes one key).

Example:
form_builder
object

Configuration for the public customer signup form at /signup/{card_id}. Lists which fields to collect, in what order, and which are required.

issue_limit
integer | null

Maximum number of customers that can be enrolled in this card. Null = unlimited. When reached, POST /v1/customers returns 403 card_limit_reached. 0 and null both mean unlimited; both read back as null.

Required range: x >= 0
Example:

1000

issued_count
integer
read-only

Current number of customers enrolled in this card (read-only). Updated on every successful POST /v1/customers.

Required range: x >= 0
Example:

234

referral
object | null

Referral program configuration. When enabled, customers can refer friends via a unique referral URL; both parties earn a bonus when the referee enrolls and completes a qualifying action.

Example:
terms_of_use
object | null

Optional terms and conditions shown on the wallet pass back side. text supports per-language (string OR object {sk, cs, en, de}) - the pass renders the customer's own language variant when present, falling back to the card's default-language text otherwise. full_url is a single URL only (no per-language variant).

registration_url
string<uri> | null

Public signup URL where customers self-enroll. Pattern: https://vexioncards.one/signup/?id={card_id}. Share via QR code, flyer, or email. Append ?loc={location_id} for per-location attribution (signup_location_id on the resulting customer). For per-location URLs of every location with this card enabled, call GET /v1/locations and check enabled_card_ids for this card's id (or read each location's cards[] (GET /v1/locations) - every entry carries its location-scoped signup_url). We previously had registration_urls_per_location on this response, but it duplicated data that lives on Location and forced an extra refresh whenever a location toggled the card on/off.

Example:

"https://vexioncards.one/signup/?id=crd_sophcrprtvpmmnwxah2lomnkee"

metadata
object

Free-form key-value map for partner-internal IDs and integration markers. NOT rendered on the wallet pass. Shape IS enforced (422 validation_failed): max 50 keys, key must match ^[a-zA-Z0-9_]{1,40}$, values must be strings of at most 500 chars. The platform never interprets the contents.

Persisted on the card template and returned on reads. Omitted entirely when empty rather than sent as null. On PATCH the map is merged per key, so sending one key never clears the others. (Distinct from Customer.metadata, which is built and live.)

Example:
status
enum<string>
default:active

Template lifecycle. draft (created but never launched - not issuable, its public signup page turns customers away), active (issuable), paused (temporarily closed to NEW signups; existing passes keep working and keep earning), archived (retired - existing customer passes keep working, no new signups). A card created through this API is born active. DELETE archives, never destroys, and historical references stay resolvable forever.

Available options:
draft,
active,
paused,
archived
Example:

"active"

image_generation
object

Server-side pass-image generation state. Standard-design cards get their wallet pass images (the per-stamp-state strip images, or the single pattern strip) rendered automatically after create and after design edits. The card is returned immediately and images render in the background, typically within seconds. processing: rendering in progress - the pass already works and images appear as soon as they are ready. completed: all images are live. failed: rendering hit an error and is retried automatically; the card itself is unaffected. not_applicable: individual-design cards (you supply the images) and cards with no generated imagery.

created_at
integer

Unix timestamp when the card was created.

Example:

1782345600

updated_at
integer

Unix timestamp when any field on the card was last modified.

Example:

1782604800

activated_at
integer | null

Unix seconds when the card FIRST went live. Stamped on the first transition to active and never overwritten, so it survives pause/reactivate and un-archive cycles. Null for a card that has never been activated (a draft).

paused_at
integer | null

Unix seconds of the most recent transition to paused. Restamped on every pause. Null for a card that has never been paused. Not cleared when the card reactivates, so it records the last pause, not the current state - read status for that.

archived_at
integer | null

Unix timestamp when the card was archived (status='archived'). Null if active.

Example:

null

Card-attached links shown on the wallet pass back side - business links (website/social) and a review-prompt link. Max 10 items.

Maximum array length: 10
Example:
currency
enum<string>

The business currency of this card. A business has one currency, so it is the same on every card. Set from the country of the first location and locked after the first transaction, customer balance or gift card purchase. See Money and currency.

Available options:
EUR,
CZK,
GBP,
HUF,
PLN,
RON,
CHF,
USD