Find-or-create customer + post transaction in one call
Resolves the customer in this fixed priority order:
- customer_id → tenant-scoped Perkstar customer
- card_serial → POS barcode or legacy pass serial → customer
- external_ref → (orgId, externalRef) lookup
- email (case-insensitive)
- phone (normalised E.164 with a legacy raw-value fallback)
First match wins. On miss, creates a customer when
create_customer_if_missing is true (the default). A missing
customer_id alone returns 404 instead of creating a replacement.
The transaction.external_transaction_id field is enforced as
permanently unique per (orgId, externalTransactionId). An exact
retry with the same canonical target, type, amount, and request fields
returns the existing transaction at HTTP 200 instead of
double-crediting, no matter how long has passed. Reusing the id for a
different request returns 409 external_transaction_id_conflict. Use
this for POS-side stable ids (Toast order #s, Shopify checkout ids,
Square payment ids). Test and live transactions cannot share the same
value; a cross-environment replay is rejected with 422. Provider-owned
prefixes such as sq_payment_ are reserved for managed integrations;
use a namespace owned by your app.
Idempotency-Key (24h replay cache) and
external_transaction_id (permanent dedupe) coexist — use
both for full robustness.
With a test credential this endpoint never creates a customer or enrolment. Resolve an existing record and card first, then post the isolated transaction. A missing customer or enrolment returns 404.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Headers
Replay-safe request key. Repeated requests with the same value within 24h return the original response unchanged.
255Body
Customer identifier block. At least ONE field must be present. Resolution order is fixed: customer_id → card_serial → external_ref → email → phone. First match wins.
Optional. Defaults to the org's single ACTIVE card. If
multiple ACTIVE cards exist, this is required (else 400
ambiguous_default_card).
Fields used when create_customer_if_missing is true and no existing customer matches the identifier.
Response
Existing transaction returned (external_transaction_id dedup hit).

