Save a Card Without Charging
Create an enrollment session so a customer saves a card first — for subscriptions or adding a payment method.
An enrollment session saves a customer's card without charging them — for subscription onboarding, adding a payment method, or any flow that needs a card on file before the first charge. It is a single-use checkout (valid up to 24 hours) that shows only the card form and a save-card button: no amount, no items, no other payment methods.
No payment is created, so the session's payment_id is 0 and no payment webhook is sent.
Enrollment sessions always use the first-generation (v1) checkout design.
If Card-on-File is enabled for your account, saving the card runs a 3D Secure check with the card issuer (a verification window may appear), and the saved card can then be used for later payments without asking for the CVV. If it is not enabled, the card is saved directly. Your integration is the same in both cases.
Step by step
Send session_type: "enrollment" and the customer. The card is saved for this customer.
curl -X POST 'https://api-stage.tonder.io/checkout/v1/sessions' \
-H 'Authorization: Token YOUR_TEST_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"session_type": "enrollment",
"customer": { "first_name": "Ana", "last_name": "Garcia", "email": "ana@example.com" },
"locale": "es",
"return_url": "https://your-store.com/card-saved"
}'curl -X POST 'https://api.tonder.io/checkout/v1/sessions' \
-H 'Authorization: Token YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"session_type": "enrollment",
"customer": { "first_name": "Ana", "last_name": "Garcia", "email": "ana@example.com" },
"locale": "es",
"return_url": "https://your-store.com/card-saved"
}'{
"id": "cs_97_0_abc123",
"url": "https://stage-payflow.tonder.io/checkout/cs_97_0_abc123",
"status": "pending",
"session_type": "enrollment",
"checkout_type": "hosted",
"payment_id": 0,
"locale": "es",
"return_url": "https://your-store.com/card-saved",
"expires_at": 1776370888,
"customer": { "first_name": "Ana", "last_name": "Garcia", "email": "ana@example.com" }
}Redirect the customer to url. They see the card form (cardholder name, card number, expiry,
CVV) and a save-card button.
With Card-on-File, a 3D Secure verification may appear first. On success the page shows a "card saved" confirmation. If something fails — verification rejected, network error, session expired, invalid card — the customer sees an error and can retry without reloading.
The session becomes completed and, with redirect_on_completion: "always", the customer is
sent to return_url. Confirm it from your backend with
Get a session:
{ "id": "cs_97_0_abc123", "status": "completed", "session_type": "enrollment", "payment_id": 0 }Fields
| Field | Type | Required | Values | Description |
|---|---|---|---|---|
session_type | string | Yes | enrollment | Makes this a card-saving session. |
customer | object | Yes | first_name, last_name, email (required); phone, address, identification (optional) | The card is saved for this customer. |
return_url | string | No | URL | Where the customer goes after saving the card. |
locale | string | No | es (default), en, zh | Language of the page. The customer can change it. |
expires_at | number | No | Unix time in seconds | 30 minutes to 24 hours from now. Defaults to 24 hours. |
external_id | string | No | — | Your reference. |
metadata | object | No | — | Any data you want back later. |
ui_config | object | No | Colors, font, shapes, labels, placeholders | Appearance for this session, on top of your saved business appearance. |
checkout_type | string | No | hosted (default), embedded | Use embedded to show the page in an iframe. |
post_message_enabled | boolean | No | true, false | Send events to your page. Requires embedded. |
redirect_on_completion | string | No | always (default), never | never keeps the customer on the page so your site handles navigation. |
Payment fields are not allowed on enrollment: amount_total, currency, line_items,
payment_method_types, payment_method_config, success_url, pending_url, cancel_url and
submit_type return 400 E004.
Embedding and events
Create the session with checkout_type: "embedded" and post_message_enabled: true, and load
url in an iframe as described in Embed the checkout. Enrollment
sessions send three events, each with session_type: "enrollment":
| Event | When |
|---|---|
checkout.initiated | The card form has loaded. |
checkout.completed | The card was saved (session_status: "completed"). |
checkout.failed | Saving failed; the customer can retry. |
Errors
| HTTP status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | E004 | A field is missing, has a value that is not allowed, or is a payment field. | Read metadata.errors, fix the body and retry. |
| 400 | E0014 | You asked for the payment of an enrollment session. | There is no payment; use Get a session. |
| 401 / 403 | — | Missing or invalid API key. | Check the Authorization header and the environment. |
| 404 | E003 | No session with this id. | Check the id and the environment. |
