Tokenization
Tokenization lets you securely store a customer's payment credentials with Rootline and charge them later — either with the customer in session for fast checkout or without the customer present for recurring subscriptions and on-demand, unscheduled charges. Rootline stores the sensitive data (PAN, expiry), and returns a non-sensitive payment token that you use as the handle for every subsequent payment.
This guide covers:
- The charge types Rootline supports today and when to use each one
- How to enroll a card and receive a payment token
- How to charge against a stored payment token, with and without the customer present
- How payment tokens, customers, and charge-type intent interact
- How to manage tokens and handle declines
Supported payment methods. Tokenization currently supports cards only.
Concepts
Payment token
A payment token is the object you store on your side as a stand-in for a payment credential. It carries a stable ID (prefixed tok_), a reference to the encrypted PAN in Rootline's vault, the card summary (BIN, last four, expiry, funding type, regionality), and the intent the token was created with — its charge_type and, when applicable, charge_type_reason.
A payment token is created only after a successfully authorized first transaction (CIT).
Customer
A customer (prefixed cus_) is an optional object representing the paying cardholder. Once a token is linked to a customer, you can:
- Retrieve all tokens belonging to that customer via
GET /v1/customers/{customer_id}/payment-tokens - Apply the "this token is only valid for this customer" policy — Rootline enforces this at charge time and rejects any charge where the token's linked customer does not match the customer on the request
- Build a returning-customer experience where saved cards are listed per customer
The public customer object on a payment carries merchant metadata used for fraud screening and display — it does not itself create or link a customer record. The fields are:
| Field | Notes |
|---|---|
name | Last name. |
first_name | First name. |
email | Email address. |
phone_number | Full international phone number. |
reference | Your own reference (e.g. internal CRM id). |
date_of_birth | YYYY-MM-DD. |
Customer records (the persistent cus_... objects) are managed through the Customer API — a separate set of endpoints listed under Managing tokens and customers.
To link a token to a customer at enrollment, create the customer first via POST /v1/customers, then pass the returned customer_id at the top level of the enrollment payment. Rootline will link the resulting token to that customer, and every subsequent charge against the token is then validated against the customer ID.
CIT vs MIT
Every payment against a stored credential is either customer-initiated (CIT) or merchant-initiated (MIT). The distinction comes from PSD2 / SCA and the card-scheme stored-credential frameworks.
| Customer-initiated (CIT) | Merchant-initiated (MIT) | |
|---|---|---|
| Who triggers the charge | The cardholder, actively | Your backend, without cardholder interaction |
| Typical example | Returning customer clicks "pay with saved card" | Monthly subscription renewal; post-ride charge |
| 3DS / SCA | Required | Out of scope — automatically skipped |
| CVC | Collected by the SDK | Must not be sent |
| Frontend involvement | Checkout Components (SDK) | None — server-to-server API call |
If the cardholder is actively present and decides to pay with a saved card, that is a CIT even though the credential is stored. Flagging it as MIT to avoid 3DS is a scheme compliance violation and can lead to disputes.
Charge type and charge-type reason
Rootline uses two fields to describe the intent behind a stored-credential transaction. These fields are passed to the card schemes so they can apply the correct stored-credential flags, interchange rates, and compliance checks.
charge_type | charge_type_reason | Flow |
|---|---|---|
credential_on_file | fast_checkout | COF CIT — returning customer pays with saved card |
credential_on_file | unscheduled | UCOF MIT — variable amount, variable timing (on-demand) |
subscription | (none — must be omitted) | Subscription MIT — recurring on a fixed schedule |
Credential-on-File and Recurring
Rootline supports three distinct tokenization flows on cards. Each has its own CIT at enrollment and its own charging semantics afterwards.
1. Credential-on-File, Fast Checkout (COF CIT)
A one-click checkout for a returning customer. The card is already stored; the customer is in session and explicitly chooses to pay with it.
- Use when: returning customer selects a saved card on your checkout page
- Initiator: cardholder (CIT)
- 3DS: required for every charge, including repeat use of the stored card
- CVC: collected via the SDK at every charge
- Frontend: required (Checkout Components)
2. Unscheduled Card-on-File (UCOF MIT)
Variable amount, variable timing. You charge the card at your discretion, after the service has been used — without the cardholder present.
- Use when: ride-hailing, on-demand delivery, EV charging, auto top-ups
- Initiator: merchant (MIT)
- 3DS: required once at enrollment; skipped for every subsequent charge
- CVC: collected at enrollment; must not be sent on subsequent charges
- Frontend: required only for enrollment; subsequent charges are server-to-server
3. Subscription Recurring (Subscription MIT)
Fixed-schedule, recurring charges. You manage the billing cycle and trigger each charge via the API; the cardholder is not present.
- Use when: monthly or annual subscriptions on a predictable cadence
- Initiator: merchant (MIT)
- 3DS: required once at enrollment; skipped for every subsequent charge
- CVC: collected at enrollment; must not be sent on subsequent charges
- Frontend: required only for enrollment; subsequent charges are server-to-server
Rootline does not operate a billing engine. You are responsible for the charging schedule, retry logic, dunning, proration, and customer notifications for subscriptions. Rootline surfaces authorization and decline data so your scheduler can act on it.
Enrolling a card
Enrollment is always a Customer-Initiated Transaction (CIT) with 3DS. The cardholder enters card details on your checkout and completes the authentication; Rootline then stores the card and returns a payment token.
Enrollment happens in two pieces:
- Server-side: you create a payment with
tokenize: trueand the desired intent (charge_type,charge_type_reason). - Client-side: the Checkout Components SDK collects card details, handles 3DS, and confirms the payment.
For the full SDK integration — including the choice between the Standard and Deferred patterns, appearance customization, event handling, and Wallet Button integration — see:
This guide focuses on the tokenization-specific parameters. Everything that follows assumes you already have a working Checkout Components integration.
Server-side: Create the enrollment payment
curl 'https://payment-api.staging.rootline.com/v1/payments' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--data '{
"account_id": "acc_123",
"amount": {
"currency": "EUR",
"quantity": "10.00"
},
"payment_rails": {
"tokenize": true,
"charge_type": "credential_on_file",
"charge_type_reason": "unscheduled"
},
"customer_id": "cus_3VfPsTP2kqnjR6LHnOdpVe",
"customer": {
"first_name": "Jane",
"name": "Doe",
"email": "jane@example.com"
},
"reference": "Card enrollment for Jane",
"return_url": "https://example.com/enrollment-complete",
"create_payment_session_secret": true
}'
| Field | Purpose |
|---|---|
tokenize | true — tells Rootline to store the card after authorization |
charge_type | Declares the intent: credential_on_file or subscription |
charge_type_reason | Required when charge_type = credential_on_file; set to unscheduled (UCOF) or fast_checkout (COF CIT). Must be omitted when charge_type = subscription. |
customer_id | Optional — the cus_... id of a customer created via POST /v1/customers. When present, the resulting token is linked to this customer and every subsequent charge is validated against it. |
customer | Optional metadata about the payer — name, first_name, email, phone_number, reference, date_of_birth. Used for fraud screening and display. |
create_payment_session_secret | true — returns a payment_session_secret for the SDK |
amount.quantity | Must be > 0. See Zero-amount enrollments. |
The response includes a payment_session_secret (prefixed pss_). Pass it to your frontend and let the Checkout Components SDK handle card collection and 3DS.
Retrieve the token after enrollment
After the customer completes 3DS and returns to your return_url, fetch the payment from your server to get the payment_token_id:
curl 'https://payment-api.staging.rootline.com/v1/payments/pmt_1oZcjWpZZM1wPn722lq2vQ' \
--header 'x-api-key: [paste-your-api-key]'
{
"id": "pmt_1oZcjWpZZM1wPn722lq2vQ",
"checkout_status": "succeeded",
"amount": { "currency": "EUR", "quantity": "10.00" },
"payment_rails": {
"payment_method": "visa",
"card_summary": {
"bin": "476173",
"last_four_digits": "0010"
},
"payment_token_id": "tok_IDBAUGBpr8TxS0Y7S99bI",
"charge_type": "credential_on_file",
"charge_type_reason": "unscheduled"
},
"customer_id": "cus_3VfPsTP2kqnjR6LHnOdpVe",
"customer": {
"first_name": "Jane",
"name": "Doe",
"email": "jane@example.com"
}
}
Store payment_token_id in your database alongside your own customer record. You can also subscribe to the payment.succeeded webhook — it carries the same payment_token_id.
When the payment was created with a customer_id, the returned token is linked to that customer. You can confirm at any time via GET /v1/payment-tokens/{id}, which returns the token's customer_id, and list all tokens for a customer via GET /v1/customers/{customer_id}/payment-tokens.
Use card_summary.last_four_digits and payment_method (e.g. "visa") to render a human-friendly label for the stored card in your UI.
Charging a stored token
Once a card is enrolled you can charge it again. How you do this depends on whether the cardholder is present.
Subscription (MIT)
Fixed-schedule, server-to-server. No SDK, no 3DS, no customer involvement.
curl 'https://payment-api.staging.rootline.com/v1/payments' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--data '{
"account_id": "acc_123",
"amount": {
"currency": "EUR",
"quantity": "29.90"
},
"payment_rails": {
"payment_token_id": "tok_IDBAUGBpr8TxS0Y7S99bI",
"charge_type": "subscription"
},
"reference": "Monthly subscription — April 2026"
}'
| Field | Notes |
|---|---|
payment_token_id | The token returned at enrollment |
charge_type | subscription |
charge_type_reason | Must not be sent for subscriptions |
amount.quantity | Must be > 0 |
card, cvc, tokenize, create_payment_session_secret | Must be omitted |
3DS is skipped automatically. A new payment is created with its own payment_id; the existing token is reused.
Unscheduled Card-On-File (MIT)
Variable amount, triggered when the service has been used. Same server-to-server pattern as subscription, but with the unscheduled reason:
curl 'https://payment-api.staging.rootline.com/v1/payments' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--data '{
"account_id": "acc_123",
"amount": {
"currency": "EUR",
"quantity": "14.50"
},
"payment_rails": {
"payment_token_id": "tok_IDBAUGBpr8TxS0Y7S99bI",
"charge_type": "credential_on_file",
"charge_type_reason": "unscheduled"
},
"reference": "Ride on 2026-04-06"
}'
Fast-Checkout (CIT)
A returning customer pays with a stored card. Because the cardholder is present, this is a CIT: 3DS is required and CVC is collected via the SDK. You create the payment with the payment token ID, then follow the checkout_url to redirect the customer. If the payment requires 3DS, it can either be performed frictionless, or through a 3DS challenge. In the frictionless case, the payment will get a final status, and your customer will be forward redirected to your return URL. In case the payment requires a 3DS challenge, this will be handled by Rootline, after which the customer is redirected to your return URL.
curl 'https://payment-api.staging.rootline.com/v1/payments' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--data '{
"account_id": "acc_123",
"amount": { "currency": "EUR", "quantity": "49.00" },
"payment_rails": {
"payment_token_id": "tok_IDBAUGBpr8TxS0Y7S99bI",
"charge_type": "credential_on_file",
"charge_type_reason": "fast_checkout"
},
"return_url": "https://example.com/payment-result",
"create_payment_session_secret": true
}'
Intent validation — what's allowed
Rootline validates every charge against the intent the token was created with. The goal is to keep scheme flagging consistent from CIT through every MIT — a token created for a subscription should not later be used to charge an unscheduled top-up.
Token intent must match request intent
Both charge_type and charge_type_reason on the token must match the values on the charge request.
If the intent does not match, the API returns a validation error indicating the mismatch, e.g. Payment token was created with charge_type=subscription, cannot be used for charge_type=credential_on_file.
Managing tokens and customers
Retrieve a token
curl 'https://payment-api.staging.rootline.com/v1/payment-tokens/tok_IDBAUGBpr8TxS0Y7S99bI' \
--header 'x-api-key: [paste-your-api-key]'
Returns the token's card summary (BIN, last four, expiry, funding type, regionalities), payment method, charge_type, charge_type_reason, and — if linked — the customer_id.
Disable a token
curl 'https://payment-api.staging.rootline.com/v1/payment-tokens/tok_IDBAUGBpr8TxS0Y7S99bI' \
--request DELETE \
--header 'x-api-key: [paste-your-api-key]'
Disables the token. Any subsequent charge against it is rejected. Disabling is permanent — you cannot re-enable a disabled token; if the cardholder wants to resume, start a new enrollment.
Customers
Rootline exposes a Customer API for creating, updating, disabling, and retrieving customers, and for listing the tokens that belong to a customer.
POST /v1/customers Create a customer
GET /v1/customers/{customer_id} Retrieve a customer
PATCH /v1/customers/{customer_id} Update a customer
DELETE /v1/customers/{customer_id} Disable a customer
GET /v1/customers/{customer_id}/payment-tokens List all active tokens for a customer
A customer record is separate from the customer object you pass on a payment request. The Customer API manages the persistent cus_... record; the customer field on a payment is metadata attached to that specific payment.
curl 'https://payment-api.staging.rootline.com/v1/customers' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--data '{
"first_name": "Jane",
"name": "Doe",
"email": "jane@example.com",
"reference": "AN64-JDOE"
}'
{
"id": "cus_3VfPsTP2kqnjR6LHnOdpVe",
"created_at": "2026-04-21T10:00:00Z",
"last_updated_at": "2026-04-21T10:00:00Z",
"first_name": "Jane",
"name": "Doe",
"email": "jane@example.com",
"reference": "AN64-JDOE"
}
Supported fields on create and update are name, first_name, email, phone_number, reference, and date_of_birth. Update is a PATCH — send only the fields you want to change.
List tokens for a customer
curl 'https://payment-api.staging.rootline.com/v1/customers/cus_3VfPsTP2kqnjR6LHnOdpVe/payment-tokens' \
--header 'x-api-key: [paste-your-api-key]'
Returns every active token linked to the customer, each with its card summary, payment method, and intent.
Linking a token to a customer
To link a newly enrolled token to a customer:
- Create the customer —
POST /v1/customersreturns acus_...id. - Pass that id as the top-level
customer_idon the enrollment payment (POST /v1/paymentswithtokenize: true).
Once linked:
GET /v1/payment-tokens/{id}returns the token'scustomer_id.GET /v1/customers/{customer_id}/payment-tokenslists every active token for the customer.- Every subsequent charge is validated against the customer reference: if the charge request carries a
customer_idthat does not match the token's linked customer, Rootline rejects the charge withPayment Token does not belong to this customer!.
curl 'https://payment-api.staging.rootline.com/v1/payments' \
--request POST \
--header 'content-type: application/json' \
--header 'x-api-key: [paste-your-api-key]' \
--data '{
"account_id": "acc_123",
"amount": { "currency": "EUR", "quantity": "10.00" },
"payment_rails": {
"tokenize": true,
"charge_type": "subscription"
},
"customer_id": "cus_3VfPsTP2kqnjR6LHnOdpVe",
"reference": "Subscription sign-up for Jane",
"return_url": "https://example.com/enrollment-complete",
"create_payment_session_secret": true
}'
Zero-amount enrollments
Zero-amount authorizations (EUR 0.00) for card verification at enrollment are not yet supported. Every enrollment transaction currently requires an amount greater than zero.
When this feature becomes available, it will let you verify and store a card without an initial charge — useful for free-trial sign-ups and pre-registering a payment method for later billing. Zero-amount charges will remain disallowed for subsequent MIT transactions; those always require a real amount.
Cardholder disclosure
Scheme rules require you to inform cardholders when their credentials are stored for future use. At minimum, your checkout page should state:
- What you are storing (payment card details)
- Why (subscription charges, on-demand top-ups, faster future checkout)
- When the card will be charged (frequency, amount variability)
- How the customer can remove their stored card
- Who processes the payment (your company, via Rootline)
Maintain a record of the cardholder's consent (timestamp and terms version). This is your responsibility as the merchant — Rootline does not collect or store consent on your behalf.
Webhooks
Enrollment and subsequent charges both produce the standard payment lifecycle events:
payment.createdpayment.succeededpayment.failed
Enrollment payments additionally produce payment_token.created once the card has been stored. The payload includes the payment_token_id, the charge_type, and the charge_type_reason — enough for you to correlate every recurring charge back to the original enrollment.
See Webhook events for the full list and payload schemas.