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.
- Authorization: Bearer <access_token> (Please contact your implementations manager to share the access token with you)
- X-CUSTOMER-ID: <paystand_customer_id> (This can be retrieved from the paystand dashboard under the integrations tab in the API Configuration Values section)
⚠ 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. |