Authorization flows
A card payment is really two events: the issuer promising the money (authorization) and you taking it (capture). Collapse them for e-commerce; split them for hotels, rentals, and delayed shipping. This page covers the whole pre-auth toolkit — incrementing, capturing in parts, and undoing at each stage.
Purchase vs. pre-authorization
The capture_method you pass at create time picks the model:
capture_method: "automatic" (purchase) | capture_method: "manual" (pre-auth) | |
|---|---|---|
| Auth + capture | One step — approval captures. | Two steps — authorize now, capture when ready. |
| Status after approval | succeeded | requires_capture |
| Customer sees | A charge | A hold, until you capture or release it |
| Time limit | — | Capture within 7 days, or the hold releases and the payment moves to canceled. |
| Methods | All | Cards only — bank redirects, wallets, and real-time rails are single-message and capture on approval. |
A pre-auth's life, drawn out — a €49.00 hold that grows to €69.00 and is captured in two parts:
Void and expiry apply from any requires_capture state — drawn once each for legibility. On networks that support multiple capture the payment stays capturable between partial captures; elsewhere the first capture closes the authorization and releases the remainder.
Incremental authorization
When the amount grows before capture — a hotel folio, a bar tab, an upsell — raise the hold instead of asking the customer to pay again. amount is the new total in minor units, not the delta; the issuer approves the difference:
curl https://api.finscale.dev/v1/payments/pay_2Xw9cJ6pF3Md/increment_authorization \
-H "Authorization: Bearer sk_test_51FinscaleDemo…" \
-H "Idempotency-Key: idem_ord_9f21_0716_incr1" \
-H "Content-Type: application/json" \
-d '{ "amount": 6900 }'
# 4900 was authorized; 6900 is the NEW TOTAL — the issuer approves the 2000 difference
{
"id": "pay_2Xw9cJ6pF3Md",
"object": "payment",
"amount": 6900,
"amount_captured": 0,
"currency": "EUR",
"status": "requires_capture",
"payment_method": "card",
"capture_method": "manual",
"reference": "ord_9f21_0716",
"provider": "prov_eu_acq_01",
"created_at": "2026-07-16T09:24:31Z",
"livemode": false
}
- Pre-authorizations only: the payment must be in
requires_capture. - The new total must exceed the current
amount— you can increment multiple times before capturing. - Not every card network supports it. Where it isn't supported — or the payment isn't a pre-auth — you get
400 invalid_request_errorwith codepayment_not_incrementable, and the original authorization is untouched.
Partial and multiple capture
POST /v1/payments/{id}/capture takes the money. Omit amount to capture the remaining authorization in full; pass less for a partial capture:
curl https://api.finscale.dev/v1/payments/pay_2Xw9cJ6pF3Md/capture \
-H "Authorization: Bearer sk_test_51FinscaleDemo…" \
-H "Idempotency-Key: idem_ord_9f21_0716_cap1" \
-H "Content-Type: application/json" \
-d '{ "amount": 3000 }'
# where the network supports multiple capture: status stays requires_capture,
# amount_captured: 3000 — capture again later for up to the remaining 3900
amountmust be ≤ the remaining authorized amount (amount − amount_captured).- Multiple capture — on supporting networks, a partial capture leaves the payment in
requires_capture;amount_capturedaccumulates across calls until the authorization is drawn down. Ideal for split shipments. - Single capture — on networks without multiple-capture support, the first capture closes the authorization; the un-captured remainder is released to the customer.
- The payment reaches
succeededwhen the authorization closes with funds captured. Refund flags then compare againstamount_captured, notamount. - Capturing a payment that isn't
requires_capturereturns400 invalid_request_error, codepayment_not_capturable.
Void vs. reversal vs. refund
Three ways money "goes back", with very different mechanics. Pick by where the payment is in its lifecycle:
| Cancel (before auth) | Void / authorization reversal | Refund | |
|---|---|---|---|
| Call | POST /v1/payments/{id}/cancel | POST /v1/payments/{id}/cancel | POST /v1/refunds |
| Payment status | requires_confirmation / requires_action | requires_capture | succeeded |
| What happens | The payment stops before any money is held. | Finscale sends an authorization reversal to the provider — the hold releases immediately instead of expiring days later. | Captured funds move back over the payment rail; a rf_ object tracks them. |
| Customer sees | Nothing | The hold disappears from their available balance. | Money re-arrives — timing varies by method. |
| Result | status: "canceled" | status: "canceled", provider: null | Status stays succeeded; amount_refunded / refunded track state. |
| Partial possible | — | No — a void is all-or-nothing. | Yes, by minor-unit amount. |
| On settlement reports | Never appears | Never appears | A negative line in the report's refunds total. |
Payments that are processing or terminal can't be canceled — refund a succeeded payment instead. See Refunds for splitting and settlement behavior.
Webhook events
payment.processing— the authorization is being routed.payment.succeeded— fires when the authorization closes with funds captured (after the final capture), not at authorization time.payment.failed— the authorization itself was declined.
There is no dedicated "authorized" event: the synchronous response (or a GET) showing requires_capture is your authorization signal. Voids and expiries surface as the payment reading canceled.
Error scenarios
| Scenario | Response |
|---|---|
Capture on a payment not in requires_capture | 400 invalid_request_error, code payment_not_capturable |
| Capture more than the remaining authorization | 400 invalid_request_error — increment first, then capture |
| Increment on a non-pre-auth, or an unsupported network | 400 invalid_request_error, code payment_not_incrementable; the original hold is unaffected |
| Increment to an amount ≤ the current total | 400 invalid_request_error — amount is the new total, and it must grow |
| Capture after the 7-day window | The payment is already canceled — the capture returns payment_not_capturable. Re-charge the customer with a fresh payment. |
Cancel a processing or terminal payment | 400 invalid_request_error — refund instead if it succeeded |
Related
- Payment lifecycle — where
requires_capturesits in the full state machine. - Refunds — after capture, this is the only way back.
- Cards & 3DS — the only method family with a two-step auth model.
- Idempotency — key every increment and capture; retries must be safe.