Pre-auth API documentation – Paystand Help Center

Overview

ℹ  NOTE
The API does not create authorizations from scratch via external calls — pre-auths originate from the Paystand checkout flow. Capture and void operations are explicitly merchant-initiated; no automatic retries are performed. All responses are synchronous.

While a pre-auth is active, Paystand blocks all additional payment attempts against the associated ERP document. Only captures from the existing authorization are allowed until it is fully captured, voided, or expired.

Authentication

All endpoints require an OAuth2 Bearer token and a customer scoping header on every request.

⚠  IMPORTANT
X-CUSTOMER-ID is required on every request. This is Paystand's internal customer ID, you can find it in your Paystand Dashboard. A pre-auth belonging to a different customer returns 404 — it is not distinguishable from a record that does not exist.

Endpoints

Create Pre-Authorization

POST /preAuths

Creates a new pre-authorization against an existing saved card. Pre-auths are normally created through the Paystand checkout flow — use this endpoint for programmatic or integration-initiated authorizations.

ℹ  NOTE
The creation response returns receivables as a raw JSON string. Call GET /preAuths/:id after creation to retrieve the enriched receivables array with per-invoice amounts, captured totals, expiration date, and invoice keys needed for subsequent captures.

Request Body

Field Type Required Description
amount number Yes Amount to authorize. Must be greater than 0.
currency string Yes Currency code, e.g. USD
cardId string Yes Paystand saved card ID. Raw processor token strings are not accepted.
expiresAt string Optional ISO 8601 expiry date. Defaults to 7 days from creation if omitted. Up to 30 days for certain MCC categories.
receivables array Optional Array of Paystand receivable/invoice IDs to link this pre-auth to.
idempotencyKey string Optional Client-supplied key to prevent duplicate pre-auth creation on retry. Scoped to this endpoint only.

Example Request

{
"amount": 1000.00,
"currency": "USD",
"cardId": "crd_abc123",
"idempotencyKey": "my-system-ref-7890",
"expiresAt": "2026-04-01T00:00:00Z"
}

Response — 200 Success

{
"id": "pa_987654",
"amount": 1000.00,
"currency": "USD",
"status": "authorized",
"amountCaptured": 0,
"expiresAt": "2026-04-01T00:00:00Z",
"authorizedAt": "2026-03-18T10:00:00Z",
// Call GET /preAuths/:id for enriched receivables data
"receivables": "[{\"id\":\"inv_001\",\"amountToPay\":\"600.00\"}]"
}

Error — 409 Duplicate Pre-Auth

{
"error": {
    "code": "validationError",
    "explanation": "A pre-authorization already exists for one or more
     of the selected receivables."
}
}

Capture Pre-Authorization

POST /preAuths/:id/capture

Triggers fund settlement against an existing pre-authorization. Supports full captures, partial captures, and multiple sequential captures until the authorized balance is fully consumed.

Option A — ERP / Invoice Capture (receivables-based)

Use when the pre-auth was created against one or more Paystand receivables. Pass the receivable IDs — the capture amount is derived automatically from the amountToPay frozen at pre-auth creation time. Fee splits are recalculated proportionally.

Field Type Required Description
receivables array Yes Array of receivable IDs to capture against. Pass plain ID strings or { "id": "..." } objects.

Full capture — all linked invoices:

{ "receivables": ["inv_001", "inv_002"] }

Partial capture — one of two linked invoices:

{ "receivables": ["inv_001"] }

Option B — Direct / Amount-based Capture

Use when the pre-auth has no linked receivables, or when you need to capture an explicit dollar amount regardless of invoice breakdown.

Field Type Required Description
amount number Yes Amount to capture. Must be greater than 0 and less than or equal to the remaining authorized balance.

Partial capture:

{ "amount": 500.00 }

Full capture — omit amount to capture full remaining balance:

{}

Response — 200 Success

{
"preAuth": {
    "id": "pa_987654",
    "status": "partially_captured",
    "amount": 1000.00,
    "amountCaptured": 500.00,
    "currency": "USD",
    "expiresAt": "2026-04-01T00:00:00Z"
},
"payment": {
    "id": "pay_11223344"
}
}

Status After Capture

Scenario preAuth.status
Captured amount < authorized amount partially_captured
Captured amount = authorized amount captured

Validation Errors — 400

Condition Error
Amount ≤ 0 or > remaining balance capture_amount must be > 0 and ≤ remaining balance
Status is not authorized, pending, or partially_captured PreAuth can only be captured when status is authorized, pending, or partially captured
Pre-auth has expired PreAuth has expired` — status also updated to expire
No remaining balance PreAuth has no remaining amount to capture
Pre-auth not found or belongs to different customer 404 NOT_FOUND
X-CUSTOMER-ID header absent 400 validationError: Customer context is required

Processor Errors — 400

Scenario Behavior
Stripe: provider hold released or amount mismatch Pre-auth marked expired. Capture failed: the authorized amount at the payment provider does not match the expected capture amount.
Stripe: other processor failure Generic providerFailureError — raw provider message is not surfaced.
Vantiv: processor failure Provider error surfaced directly.
✕  WARNING
On any processor failure, the pre-auth status and amountCaptured are not modified. No payment record is created and no ERP record is posted.
⚠  IMPORTANT
The capture endpoint does not currently support a per-capture idempotency key. A duplicate POST while the pre-auth is in partially_captured state will re-execute. For full captures, the second call will be rejected because the status will already be captured.

Void Pre-Authorization

POST /preAuths/:id/void

Cancels the authorization at the payment provider and releases any remaining hold on the payer's card. No payment record is created and no GL impact occurs. Voiding is significantly cheaper than issuing a refund.

No request body is required.

Response — 200 Success

{
"id": "pa_987654",
"status": "voided",
"amount": 1000.00,
"amountCaptured": 0,
"currency": "USD"
}

Void Behavior by Current Status

Current status Behavior
authorized / pending Provider cancel called → status set to voided
partially_captured Provider cancel called (Stripe: capture with amount_to_capture: 0) → status set to voided
voided No-op — returns current record as-is (idempotent)
expired No-op — returns current record as-is (idempotent)
captured Rejected — Cannot void an already fully captured pre-auth (400)

List Pre-Authorizations

GET /preAuths

Returns an array of PreAuth objects scoped to the authenticated customer. Accepts a standard LoopBack filter object via ?f=.... Supports where, limit, offset, and order.

Get Pre-Authorization

GET /preAuths/:id

Returns the fully enriched PreAuth object — including the resolved receivables array, expiration date, payer details, captured amounts, and full payment history. Use this after creation to get enriched data.

Response — 200 Success

{
"id": "pa_987654",
"amount": 1000.00,
"currency": "USD",
"status": "partially_captured",
"amountCaptured": 600.00,
"expiresAt": "2026-04-01T00:00:00Z",
"authorizedAt": "2026-03-18T10:00:00Z",
"hasLinkedReceivables": true,
"payerDisplayName": "Acme Corp",
"payerEmail": "billing@acme.com",
"receivables": [
    {
      "id": "inv_001",
      "amountToPay": "600.00",
      "amountRemaining": 0.00,
      "amountCaptured": 600.00,
      "invoiceKey": "INV-2026-001",
      "status": "captured"
    },
    {
      "id": "inv_002",
      "amountToPay": "400.00",
      "amountRemaining": 400.00,
      "amountCaptured": 0,
      "invoiceKey": "INV-2026-002",
      "status": "open"
    }
],
"capturedPayments": [
    {
      "id": "pay_11223344",
      "amount": 600.00,
      "status": "posted",
      "datePaid": "2026-03-18T12:00:00Z",
      "currency": "USD"
    }
]
}

Receivable Status Values

Status Meaning
captured amountCaptured >= amountToPay — fully settled
released Pre-auth was voided before this receivable was captured
other values Reflects the underlying Paystand Receivable status, e.g. open, paid

PreAuth Object Schema

Field Type Description
id string Unique pre-auth identifier
amount number Originally authorized amount
currency string Currency code
status string See Status Values section below
amountCaptured number Total captured to date — accumulates across partial captures
expiresAt string (ISO 8601) When the authorization expires. Visible in the Paystand Dashboard and returned via GET /preAuths/:id.
authorizedAt string (ISO 8601) When the provider confirmed the hold
cardId string Paystand saved card ID used for the authorization
provider string Payment processor: stripe or vantiv
receivables array Linked receivables with id, amountToPay, currency, invoiceKey. The amountToPay is frozen at creation time and drives Option A capture amounts.
capturedPaymentIds string All payment IDs from all captures against this pre-auth
idempotencyKey string Client key supplied at creation time — prevents duplicate auth creation on retry
metadata object Arbitrary extra data

Status Values

Status Description
pending Created — provider authorization is in flight
authorized Provider hold active. No capture yet. Funds are reserved. Expiration date is set.
partially_captured One or more partial captures completed. Remaining balance still available. Authorization remains open.
captured Fully captured. No remaining balance. Pre-auth is closed.
voided Authorization cancelled at the provider. Hold released. No funds moved.
expired Expiry date passed or provider hold released. A new authorization must be created.
ℹ  NOTE
While a pre-auth is in an active status (authorized, pending, partially_captured), Paystand blocks all additional payment attempts against the associated ERP document from any other payment source. Payment capability is restored once the authorization is fully captured, voided, or expired.

Prev Next

Table of contents

Pre-auth API documentation