Initialize a customer-paid scan payment

Creates a pending PaymentTransaction (purpose=scan) for a vendor whose
scan_payment_bearer is customer, then returns the data the widget needs to launch
the gateway.

  • Currency NGN → Paystack inline. Response carries paystack.public_key; the widget
    opens the Paystack inline checkout client-side using reference and amount.
  • Any other currency → Startbutton hosted checkout. Response carries
    startbutton.checkout_url; the widget opens this URL in a new tab.

Idempotent on (vendor_id, batch_id) while the row is pending — replaying the request
returns the existing reference instead of stacking orphans. The exceptions are
has_body_addon and scan_type: a replay that changes either is re-priced onto a new
reference, and the superseded row is abandoned so its open checkout cannot collect the
old amount.

The breakdown block exposes the gateway fee added on top of the scan price (Oyster's
rate for the scan type, the vendor's markup, and the flat body fee when
has_body_addon is set). The
widget uses this to display "Subtotal + Fee = Total" before launching the gateway.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
Body Params
uuid
required

UUID v7 generated by the widget at scan start. The same value is sent to
/skin/analyze later as client_batch_id and becomes the SkinImageAnalysis batch id.

string
required
length ≤ 255
string | null
string | null
string
enum
Defaults to face

What is being scanned. body prices a body scan standing on its own, at the
vendor's body rate — not the face rate, and not the flat body add-on fee,
which prices body work sitting on top of a face scan.

Send body only for a session with no face capture at all. A session that
captures a face and body zones is face with has_body_addon: true; sending
both would charge a session and an add-on for one scan, and the two are
mutually exclusive.

Refused with 422 when the vendor does not offer the type.

Allowed:
boolean
Defaults to false

Whether this session also captures hands/feet. When true the quote adds the
vendor's flat body-scan fee on top of the face rate — one fee per session,
however many parts are captured.

Send it based on what will actually be analysed, not on what the customer
picked at the offer: a part they selected but never finished capturing is not
delivered, and the fee would bill them for nothing.

Ignored when the vendor has body scanning switched off, since the API refuses
those body scans anyway. Omitting it prices a face scan.

boolean
Defaults to false

Buy the vendor's multi-scan pack instead of a single scan. Only valid when
GET /scan-payments/entitlement returned a non-null pack; anything else
is refused with 422.

The whole pack is paid for and settled here. Redeeming a slot later charges
nobody. A pack is priced for what its slots cover (see the entitlement's
pack.coverage), so has_body_addon and scan_type do not apply to it.

422 when the vendor does not sell packs; 402 when the vendor cannot currently
carry one (their prepaid credit or monthly spend cap will not cover every scan
in it), in which case offer a single scan instead.

string | null

A pack the customer ALREADY holds, whose slot covers this session's face scan.
Buys the body add-on fee on its own, because the slot has paid for the face
half. Send it with has_body_addon: true and batch_id set to the SESSION's
batch — the same one the face scan runs under. One top-up then covers every
body zone of that session, which ride on it via covered_by_batch_id exactly
as they would on an ordinary face payment. The face scan itself still presents
the pack's own reference.

The fee is Oyster's add-on rate alone: no vendor markup (earned once when the
pack sold) and no external retail rule (those price a whole scan).

409 when the pack already covers body, or when the vendor charges nothing for
an add-on — in both cases the body scan runs without a payment. 422 when the
reference is unknown, spent, expired, does not cover face, or is sent with
pack: true or a body-only scan_type.

Headers
string
required

Vendor slug — used to resolve the vendor.

string
enum
required
Allowed:
Responses

Language
Credentials
Bearer
JWT
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json