Skip to main content

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

FieldTypeAlways PresentDescription
idstringYesPayment intent ID — vpi_test_* (sandbox) or vpi_live_* (live).
statusstringYesCurrent lifecycle status. One of requires_action, authorized, captured, succeeded, voided, failed (see Status lifecycle).
amountintegerYesAmount in minor units (cents), ≥ 0.
currencystringYesISO 4217 currency code (3-letter uppercase).
capture_methodstringYesautomatic — capture immediately on a successful authorization. manual — authorize now, capture later via /capture.
next_actionobject | nullNoPresent 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_confirmobject | nullNoEmbedded-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_codestring | nullNoNormalized decline reason on a failed intent (see Decline codes).
cardobject | nullNoPCI-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_atstring | nullNoISO 8601 creation timestamp.
metadataobjectNoMerchant-provided key-value pairs you set at creation.
pulseTokenstring | nullNoShort-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.
Vendor-neutral by design

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

StatusMeaning
requires_actionA 3D Secure (or other) challenge must complete before the intent can settle. Resolve next_action (redirect flow) or client_confirm (embedded flow).
authorizedFunds are held but not captured. Only reachable with capture_method: "manual" — capture with /capture or release with /void.
capturedA previously authorized intent was captured — money has moved.
succeededAuthorized and captured in one step (capture_method: "automatic"). Money has moved.
voidedAn authorized intent was cancelled before capture. No money moves.
failedThe 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.url and send the buyer there.
  • Embedded-checkout integrations read client_confirm.client_secret and 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.