Tokenization & Saved Cards
How a card becomes a reusable Cresora vault token on the hosted page, and how to charge it server-side.
Cards are captured on the Cresora hosted payment page (HPP) and nowhere else. You create an
HPP session with tokenize: true, the payer enters the card on the hosted page and consents to
saving it, and the hpp_session.completed webhook delivers a Cresora vault token — an opaque
reference with the prefix cvt_. Your server stores that token and charges it later through
POST /api/v1/transactions. Raw gateway tokens are never delivered, and raw PAN is never
accepted at the transaction boundary.
There is no client-side tokenization SDK and no direct card-tokenize API. If you are reading older documentation that describes a browser JavaScript library for collecting card data, that product does not exist — the hosted page is the only card-capture surface.
Save a card
Both flows are HPP sessions created with tokenize: true. See the HPP guide for
the full session contract.
Charge and save in one flow — page_purpose: "payment":
curl -X POST https://api.cresoracommerce.ai/api/v1/hpp/sessions \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_$(uuidgen)" \
-d '{
"merchant_id": "0190a1b2-c3d4-7e5f-8901-23456789abcd",
"amount": "50.00",
"currency": "USD",
"tokenize": true
}'Save without charging — page_purpose: "save_card" runs a $0 verification and vaults the
card. Omit amount, or send "0.00":
curl -X POST https://api.cresoracommerce.ai/api/v1/hpp/sessions \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_$(uuidgen)" \
-d '{
"merchant_id": "0190a1b2-c3d4-7e5f-8901-23456789abcd",
"tokenize": true,
"page_purpose": "save_card",
"credential_usages": ["unscheduled_cof"]
}'Because the payer enters card data on Cresora-served pages, that data never transits your systems. Which PCI validation requirement applies to your business is a question for your QSA or compliance advisor — this page does not determine your SAQ eligibility.
Receive the token
The vault token arrives on the hpp_session.completed webhook:
{
"envelope_version": 1,
"event_type": "hpp_session.completed",
"data": {}
}The elided data object carries the token as data.cresoraVaultToken — for example
cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42. Treat it as opaque; its only use is as
vault_token on a later charge.
stored_credential.vaulted fires only when a new credential is vaulted for the first time.
Use hpp_session.completed as the per-save signal — it carries the vault token every time,
whether or not the credential was newly created.
Charge the saved card
curl -X POST https://api.cresoracommerce.ai/api/v1/transactions \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: idem_$(uuidgen)" \
-d '{
"type": "SALE",
"merchant_id": "0190a1b2-c3d4-7e5f-8901-23456789abcd",
"amount": "50.00",
"vault_token": "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
"use_type": "ONE_TIME_FUTURE"
}'amount, vault_token, and use_type are required, alongside merchant_id and type.
Amounts are decimal strings. A card object at this boundary is rejected with 400.
use_type declares who initiated the charge, and that changes how it is priced:
use_type | Initiated by | Behavior |
|---|---|---|
ONE_TIME_FUTURE | Cardholder — customer present, paying with their saved card | Surcharge and convenience fee computed server-side; merchant AVS policy applied |
RECURRING | Merchant | Billed as-is — no fees, no AVS policy |
INSTALLMENT | Merchant | Billed as-is — no fees, no AVS policy |
UNSCHEDULED_COF | Merchant | Billed as-is — no fees, no AVS policy |
Scope and consent
Scope is set at save time through credential_usages on the HPP session, using the lowercase
forms recurring, installment, unscheduled_cof, and one_time_future. Every charge then
declares a use_type, and that intent must sit inside the scope the credential was saved
with. Charging outside it returns 422 stored_credential_scope_violation.
A credential is one consent, not one save:
- Re-saving the same card under the same consent returns the same
idand the samevault_token— the token is stable across re-saves — and fires nostored_credential.vaulted. - Saving the same card with a different scope creates a separate entry.
- Revocation is final. A revoked token is never reissued.
Two entries for the same card can share the same card.last4, so last4 alone cannot tell them
apart. Key your own records on id or vault_token.
Manage credentials
These endpoints require the stored_credential:read, stored_credential:revoke, and
stored_credential:charge permission scopes.
List credentials for a merchant — optionally narrowed to one customer_id, with cursor and
page_size for pagination:
curl https://api.cresoracommerce.ai/api/v1/merchants/0190a1b2-c3d4-7e5f-8901-23456789abcd/stored-credentials \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx"The response is { "data": [...], "pagination": {...} }. Fetch one entry with
GET /api/v1/merchants/{merchantId}/stored-credentials/{storedCredentialId}.
Each StoredCredential always carries id, vault_token, merchant_id, partner_id, rail,
card (including last4), scope, enrollment_terms, consent_captured, state, created_at,
and version. customer_id and processor are optional. scope is an array of RECURRING,
INSTALLMENT, UNSCHEDULED_COF, or ONE_TIME_FUTURE and may be empty. state is one of
ACTIVE, REVOKED, or EXPIRED. consent_captured is a boolean — the underlying gateway
consent identifier stays backend-only and is never returned.
Revoke a credential. reason is optional, capped at 500 characters, and recorded in the audit
trail. The response is 204, the state moves ACTIVE → REVOKED, and the vault token can no
longer be charged:
curl -X DELETE "https://api.cresoracommerce.ai/api/v1/merchants/0190a1b2-c3d4-7e5f-8901-23456789abcd/stored-credentials/019e91c2-1f08-7a55-b3d1-88e0c4a72f61?reason=customer%20request" \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx"No Idempotency-Key is required — DELETE is idempotent per RFC 9110.
Errors
| Code | HTTP | What it means |
|---|---|---|
stored_credential_scope_violation | 422 | The use_type falls outside the scope the credential was saved with. Charge within scope, or have the payer save the card again with the usage you need. |
stored_credential_not_chargeable | 422 | A legacy entry that cannot be charged. Not retryable — the payer must re-establish the card through a save-card flow. |
raw_card_not_supported | 400 | A card object was sent to POST /api/v1/transactions. Raw PAN is never accepted; capture the card on the hosted page instead. |
3-D Secure (3DS2)
How 3DS2 card authentication works on the Cresora platform — where challenges are actually issued today, and the reserved THREE_DS_PENDING state and completion endpoint contract.
Surcharging Guide
How the credit-card surcharge program works — enablement, state rules, and where the amounts appear.