Tokens
POST /v1/tokens creates a reusable payment-method token. Token IDs use the vp_pmt_test_* / vp_pmt_live_* prefix.
This is the server-side token endpoint. For the browser/embedded flow — where the Embedded Fields SDK (vora.js) mints a token from card fields the buyer types — see Tokenization.
Required key: secret key (vp_sk_*).
Create a token
curl https://checkout.vonpay.com/v1/tokens \
-H "Authorization: Bearer vp_sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "buyer_id": "buyer_abc", "provider_reference": "pm_token_abc123" }'
const token = await client.tokens.create({
buyerId: "buyer_abc",
providerReference: "pm_token_abc123",
});
Request — CreateTokenRequest
All fields are optional, and the schema is strict (unknown fields are rejected).
| Field | Type | Required | Description |
|---|---|---|---|
buyer_id | string | — | Buyer to associate the token with (enables reuse / MIT). |
buyer | object | — | Nested buyer profile (snake_case): external_id, email, name, phone, company, address, metadata. Upserts the same buyer record as POST /v1/buyers and associates the vaulted token with it — buyer.external_id feeds the same buyer association as the flat buyer_id, which remains supported. Must carry an identity (buyer.external_id or buyer.email) directly or via buyer_id, else 400 validation_error. Sending buyer_id AND buyer.external_id with equal values is fine; different values return 400 validation_error. Echoed back on the response (present only when sent; buyer.metadata echoed post-scrub). |
provider_reference | string (≤ 255) | Yes | A gateway-specific reference for the payment method — depending on the underlying gateway, an iframe-vault payment-method handle or a server-side setup reference. Required in both modes: without it the request returns 400 validation_error. Max 255 chars. |
card_last4 | string | — | Accepted for compatibility. Ignored when the payment provider supplies the saved card's last four digits, which is the usual case. |
card_brand | string: visa | mastercard | amex | discover | unionpay | jcb | diners | unknown | — | Accepted for compatibility. Ignored when the payment provider supplies the saved card's network. |
exp_month | integer | — | 1–12. Accepted for compatibility. Ignored when the payment provider supplies the saved card's expiry. |
exp_year | integer | — | Four-digit year (≥ 2025). Accepted for compatibility. Ignored when the payment provider supplies the saved card's expiry. |
metadata | object | — | Record with string keys and arbitrary JSON values (size-capped, scrubbed). |
setup_for_future_use | string: off_session | on_session | — | Permission to charge the card again — off_session or on_session. Omitted, the token is single-use and a merchant-initiated charge against it is refused with payment_method_consent_missing. Recorded once at vault time; nothing adds it later. |
allow_redisplay | string: always | limited | unspecified | — | Permission to show the card back to the buyer in a later checkout — always, limited, or unspecified. A different permission from setup_for_future_use above: agreeing to a subscription is not agreeing to see that card offered on an unrelated purchase months later. Only always permits display. Send unspecified when you did not ask — that is a real answer, and omitting the field is not. Like consent, it can only be captured on the call that first vaults the card and can never be added afterwards. Echoed back on the response when sent. |
billing_address | object | — | Buyer billing address, stored encrypted with the saved card and never returned; correct it later with PATCH /v1/tokens/{id}. No verification result is produced at vault time, because no payment is made. A later POST /v1/payment_intents charge on this card that sends no billing_address uses the stored one, on payment connections that support address verification; an address sent on the charge takes priority. One payment connection cannot take an address on the charge at all and refuses the whole charge with 422 capability_not_supported when billing_address is present; see Address verification. See Address verification. |
stored_credential_use | string: recurring | installment | unscheduled | — | Declares what the stored card is for — recurring, installment, or unscheduled. The server-to-server counterpart of storedCredentialUse on POST /v1/sessions — note the different casing on each surface. Use the same value you will later send as mit.reason. Reaching the card networks additionally requires currency and buyer_id on this request plus account-level enablement; when it does not, the token is still vaulted and the response carries a warnings entry rather than an error. See Recurring & saved cards. |
currency | string (exactly 3) | — | Three-character currency for the zero-amount card verification. Vaulting a card creates no payment, so on its own there is nothing to carry a stored_credential_use declaration to the card networks; supplying a currency lets the vault run as a zero-amount verification that does. Deliberately un-defaulted — there is no correct fallback currency. Omit it and the card is vaulted with no verification. |
Test and live keys behave the same: both require a provider_reference (the payment method must already exist at the gateway, or in your sandbox account's processor test environment). Without it, the request returns 400 validation_error. A test key with no test environment behind it is refused with 422 sandbox_account_required.
Response — PaymentMethodToken
{
"id": "vp_pmt_test_QAqnXEJF_TCum1jg",
"status": "active",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2030
}
}
| Field | Type | Description |
|---|---|---|
id | string | Token ID (vp_pmt_test_* / vp_pmt_live_*). |
status | string: active | revoked | Token lifecycle state. |
setup_for_future_use | string: off_session | on_session | Echoes the reuse permission you sent on the request, so you can confirm it was recorded against the token. Omitted — not null — when you did not send one. |
card.brand | string | null | Card network (e.g. visa), read from the payment provider's record of the saved card. null if it could not be read; the card is still saved. |
card.last4 | string | null | Last four digits, read from the payment provider. null if they could not be read. |
card.exp_month | integer | null | 1–12, read from the payment provider. null if it could not be read. |
card.exp_year | integer | null | Four-digit year, read from the payment provider. null if it could not be read. |
idempotent | boolean | true when this response is a replay of an earlier request that used the same Idempotency-Key — no new token was vaulted, you are being handed the existing one. It only appears when you sent that header; without one, a retry vaults a second token. See Idempotency. |
allow_redisplay | string: always | limited | unspecified | Echoes the display permission you sent. Present only when you sent it. |
buyer | object | Echoes the nested buyer you sent, confirming it was accepted. buyer.metadata comes back after privacy scrubbing, so it reflects what was stored rather than exactly what you sent. Present only when you sent one. |
warnings | array of object | Present only when something about the request did not take effect. A warning never means the vault failed — the token in the same response is real and usable. Each entry carries code, message, and field. |
Warning codes
| Code | Meaning |
|---|---|
stored_credential_use_not_applied | The card was saved, but your stored_credential_use declaration did not reach the card networks, so no stored-credential declaration was made for this card — a later merchant-initiated charge on this token will not be anchored to one. Reaching the networks requires currency and buyer_id on the request plus account-level enablement; contact support to confirm enablement before relying on the declaration. |
List a buyer's saved cards
GET /v1/payment_methods?buyer_id=<your customer reference>
Returns the cards you have on file for one of your customers, newest first. Secret key only — these are handles that can be charged, so a publishable key is refused.
buyer_id is your own customer reference — the same value you send as
buyerId when creating a session and as buyer_id when creating a payment
intent. You do not need to resolve an id of ours first.
A card saved during a payment that finished asynchronously — anything that went through 3-D Secure, or a wallet payment recovered after a delay — is vaulted after your original call returned, so its token was never in that response. This is how you find it afterwards.
| Parameter | ||
|---|---|---|
buyer_id | required | Your own customer reference (max 200 chars). |
limit | optional | 1–100, default 25. Values above 100 are capped. |
starting_after | optional | The next_cursor from a previous response. A malformed value is rejected rather than silently returning the first page again. |
The response is { data, next_cursor, has_more }, newest first. next_cursor is
null on the last page. Each entry carries:
| Field | Notes |
|---|---|
id | vp_pmt_* — the only handle returned (never a provider-side one); pass back as payment_method.id when charging. |
status | Always active. A revoked card is omitted, not listed as revoked. |
card | brand, last4, exp_month, exp_year — display identity only. Each is null if the payment provider's details could not be read when the card was saved. |
wallet_type | Set when the card was saved through a wallet. |
setup_for_future_use | Whether you may charge it again. |
allow_redisplay | Whether it may be shown back to the buyer. |
Both permissions are returned as data, not applied as a filter — a card without setup_for_future_use: "off_session" is listed and a merchant-initiated charge against it is refused (the two permissions). A publishable key cannot list saved cards: read them on your server and send your frontend only what it needs to render.
Update a saved card's billing address
PATCH /v1/tokens/{id}
Replaces the billing address stored with a saved card, for a customer who has moved or a typo made when the card was saved. Secret key only. Each saved card holds its own address, so updating one never changes another card belonging to the same customer.
{
"billing_address": {
"address_line1": "1600 Pennsylvania Ave NW",
"city": "Washington",
"state": "DC",
"postal_code": "20500",
"country": "US"
}
}
billing_addressis the only field you can send. Card details come from the processor, and the two reuse permissions are recorded once when the card is saved, so any other field returns400 validation_unknown_field.- Send
"billing_address": nullto clear the stored address. Leaving the field out returns400 validation_missing_field: a request that changes nothing is treated as a mistake. - The address is never returned, by this endpoint or any other. The response reports
has_billing_address, read back from what was stored. - How the stored address is used. A later
POST /v1/payment_intentscharge on this card that sends nobilling_addressuses the stored one, on payment connections that support address verification (the charge response then carries anavs_result_code). An address sent on the charge takes priority, so if you already send one on every charge nothing changes. One payment connection cannot take an address on the charge at all and refuses the whole charge with422 capability_not_supportedwhenbilling_addressis present; see Address verification. Charges started from your checkout page use the address given on that page. - A card saved without an address picks one up from its first approved
POST /v1/payment_intentscharge that sends abilling_address, unless that charge's address check came back as no match. On a card saved for a buyer, the charge must name that same buyer. Use this endpoint to correct or clear it. - Keep it current. The stored address is checked by the card's bank on later charges, and an out-of-date one fails the check, which some banks treat as a reason to decline. If you know it is wrong but not yet the new one, clear it with
null: a card with no stored address simply gets no address check.
{
"id": "vp_pmt_live_3xK9vQ2mNp7wRtY4",
"status": "active",
"card": { "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2030 },
"has_billing_address": true
}
In the server SDKs (2.6.0 or later) setting and clearing are separate methods, so an undefined variable can never clear an address by accident:
await vonpay.tokens.updateBillingAddress("vp_pmt_live_3xK9vQ2mNp7wRtY4", {
addressLine1: "1600 Pennsylvania Ave NW",
city: "Washington",
state: "DC",
postalCode: "20500",
country: "US",
}); // → { id, status, card, hasBillingAddress: true }
await vonpay.tokens.clearBillingAddress("vp_pmt_live_3xK9vQ2mNp7wRtY4");
The SDKs check the address before sending: a missing or blank addressLine1, postalCode or country throws (TypeError in Node, ValueError in Python) and nothing is sent. Both SDKs also refuse unknown keys, so a typo such as address1 fails instead of being dropped. To remove an address on purpose, use clearBillingAddress().
vonpay.tokens.update_billing_address("vp_pmt_live_3xK9vQ2mNp7wRtY4", {
"address_line1": "1600 Pennsylvania Ave NW",
"city": "Washington",
"state": "DC",
"postal_code": "20500",
"country": "US",
})
vonpay.tokens.clear_billing_address("vp_pmt_live_3xK9vQ2mNp7wRtY4")
| Status | Code | Cause |
|---|---|---|
400 | validation_unknown_field | A field other than billing_address was sent. |
400 | validation_missing_field | billing_address was left out. |
400 | payment_method_mode_mismatch | A test card with a live key, or the reverse. |
403 | auth_key_type_forbidden | A publishable key was used. |
404 | payment_method_not_found | No such saved card on your account. |
413 | validation_error | The body is larger than 1 KiB. |
415 | unsupported_media_type | The request did not declare Content-Type: application/json. |
422 | payment_method_inactive | The card is revoked. A revoked card is not a missing one, so this is not a 404. |
Revoke a stored payment method
DELETE /v1/tokens/{id}
Revokes a stored card so it can never be charged again. Secret key only.
Once revoked, a later charge against that token fails with
payment_method_inactive, and the card
stops appearing in the saved-cards list.
⚠️ Revoking is final. There is no un-revoke call — the buyer has to present the card again, and a card re-saved that way is a new token with new permissions.
Status
| Status | Meaning |
|---|---|
active | The token can be used to create a payment intent. |
revoked | The token has been revoked and can no longer be used. |
A token does not carry a separate expired status. Card expiry (exp_month / exp_year) is stored on the token's card object, but the token itself stays active until it is revoked.
Errors
| Status | Code | Cause |
|---|---|---|
400 | validation_error | The request had no provider_reference (or another invalid request field). |
501 | endpoint_not_implemented | The merchant's gateway does not expose tokenization through this endpoint — some gateways accept only browser-minted handles and bypass /v1/tokens entirely. |
Using a token
Pass a token's id as payment_method.id when creating a payment intent ({ "payment_method": { "id": "vp_pmt_..._" } }). See Payment Intents.
Related
- Tokenization — minting tokens browser-side with the Embedded Fields SDK
- Payment Intents — charging a token
- Test mode — what a test key reaches; the cross-mode rule