Payment Intent Object
A payment intent is the server-side record of one payment's lifecycle — authorize, capture, void, refund — driven entirely from your backend. You create one with POST /v1/payment_intents; it's what every integration path ultimately charges through. This page documents the object's shape. For the how-to (auth/capture/void/refund, MIT, 3DS), see the Payment Intents guide.
{
"id": "vpi_live_8x4n2pq7m1",
"status": "succeeded",
"amount": 4999,
"currency": "USD",
"capture_method": "automatic",
"card": { "brand": "visa", "last4": "4242" },
"created_at": "2026-06-23T15:30:00.000Z"
}
Core fields
| Field | Type | Always Present | Description |
|---|---|---|---|
id | string | Yes | Payment intent ID — vpi_test_* (sandbox) or vpi_live_* (live). |
status | string | Yes | Current lifecycle status. One of requires_action, authorized, captured, succeeded, voided, failed (see Status lifecycle). |
amount | integer | Yes | Amount in minor units (cents), ≥ 0. |
currency | string | Yes | ISO 4217 currency code (3-letter uppercase). |
capture_method | string | Yes | automatic — capture immediately on a successful authorization. manual — authorize now, capture later via /capture. |
next_action | object | null | No | Present when a server-redirect 3DS challenge is required: { "type": "redirect_to_url", "redirect_to_url": { "url": "…" } }. Redirect the buyer to url; the intent settles after the challenge. |
client_confirm | object | null | No | Embedded-checkout 3DS handle: { binder, client_secret }. Populated when status is requires_action on gateways that support client-side confirmation. Forward client_secret to the browser SDK (collection.submit({ paymentIntent: { id, action: { client_secret } } })). Coexists with next_action. |
decline_code | string | null | No | Normalized decline reason on a failed intent (see Decline codes). |
card | object | null | No | PCI-safe card presentation — { "brand": …, "last4": … }. brand is one of visa, mastercard, amex, discover, diners, jcb, unionpay, unknown; last4 is 4 digits. No PAN, BIN, or fingerprint is ever included. |
created_at | string | null | No | ISO 8601 creation timestamp. |
metadata | object | No | Merchant-provided key-value pairs you set at creation. |
pulseToken | string | null | No | Short-lived signed token for the real-time status subscription — forward it to the browser so the SDK's subscribeToPaymentIntent can resolve handleAction() over Server-Sent Events the instant a terminal status lands. null when the real-time substrate is unavailable; fall back to your timeout path. Opaque to your code — don't log it. |
The payment intent has the same shape regardless of which gateway processes it — your code never branches on a processor. Raw vendor decline codes are normalized into decline_code; client_confirm exposes only a client_secret you forward verbatim. Processor selection happens server-side via VORA.
Status lifecycle
| Status | Meaning |
|---|---|
requires_action | A 3D Secure (or other) challenge must complete before the intent can settle. Resolve next_action (redirect flow) or client_confirm (embedded flow). |
authorized | Funds are held but not captured. Only reachable with capture_method: "manual" — capture with /capture or release with /void. |
captured | A previously authorized intent was captured — money has moved. |
succeeded | Authorized and captured in one step (capture_method: "automatic"). Money has moved. |
voided | An authorized intent was cancelled before capture. No money moves. |
failed | The authorization or capture failed. See decline_code for the reason. |
┌──► captured ──► (refund via POST /v1/refunds)
requires_action ──► authorized ──► voided
│ └──► (manual capture only)
▼
succeeded ◄── (automatic capture)
│
failed (decline_code set)
The client-side result of a charge is a UX signal only. Always confirm settlement server-side via the payment_intent.succeeded / charge.succeeded webhook before fulfilling.
Decline codes
When status is failed, decline_code carries the normalized reason (branch on this rather than a raw issuer code):
card_declined · insufficient_funds · expired_card · incorrect_cvc · incorrect_zip · card_velocity_exceeded · fraudulent · stolen_card · lost_card · do_not_honor · issuer_unavailable · processing_error · generic_decline
It's null on any non-failed intent. See Error codes for the full decline-handling guidance.
3D Secure: next_action vs client_confirm
A requires_action intent populates one or both of these, depending on how you integrate:
- Server-redirect integrations read
next_action.redirect_to_url.urland send the buyer there. - Embedded-checkout integrations read
client_confirm.client_secretand forward it to the browser SDK, which renders the harmonized 3DS modal in place.
Both can populate on the same response — consume the one that matches your integration. client_confirm.client_secret is bearer-equivalent for the intent it's bound to; never log or persist it beyond the request/response round-trip. See Embedded Fields → 3D Secure.
Related
- Payment Intents guide — the auth / capture / void / refund lifecycle, MIT, and recurring
- Session Object — the hosted-checkout session record
- Refunds — refunding a captured intent
- Error codes —
decline_codehandling and recovery - Webhooks —
payment_intent.*event payloads