Stripe Forward
A single endpoint, POST /connections/stripe-forward/tokenize, that accepts forwarded payment-method data from Stripe's Forwarding API and creates the appropriate Basis Theory resource:
| Forwarded payment method | wallet_type | Resource created |
|---|---|---|
| Card (FPAN) | absent | Token of type card |
| Apple Pay™ DPAN | apple_pay | Apple Pay™ resource |
Google Pay™ DPAN (CRYPTOGRAM_3DS) | google_pay | Google Pay™ resource |
Google Pay™ FPAN (PAN_ONLY) | absent | Token of type card |
See Choosing the wallet type below for guidance on selecting the right shape for each Stripe Payment Method.
Tokenize a Card
Endpoint conforming to Stripe's API contract to use their Forwarding API and create a card token.
This endpoint differs from the Create Token endpoint by conforming to Stripe's Forwarding API
contract: card contains Stripe's payment method details, and metadata contains any Basis Theory specific token attributes that
were specified when calling the Forwarding API.
The card object will then be transformed to the Basis Theory's card token data, so any expression used in the original
request to the Forwarding API referring to card data must reference the final transformed data object as shown in the example request below.
Specifically, in line 16 when referencing the card's expiration month, it is data.expiration_month and not card.exp_month.
{
"data": {
"number": "...",
"expiration_month": "...",
"expiration_year": "...",
"name": "..."
}
}
Permissions
token:create
Request
- cURL
curl "https://api.basistheory.com/connections/stripe-forward/tokenize" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"card": {
"number": "4242424242424242",
"exp_month": "07",
"exp_year": "2030",
"cvc": "123",
"name": "John Doe"
},
"metadata": {
"mask": {
"number": "{{ data.number | reveal_last: 4 }}",
"expiration_month": "{{ data.expiration_month }}",
"expiration_year": "{{ data.expiration_year }}",
"name": "{{ data.name }}"
},
"containers": [ "/pci/high/" ],
"metadata": {
"nonSensitiveField": "Non-Sensitive Value"
},
"search_indexes": [
"{{ data.number | last4 }}"
],
"fingerprint_expression": "{{ data.number }}",
"deduplicate_token": true,
"expires_at": "8/26/2030 7:23:57 PM -07:00"
}
}'
Request Parameters
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
card | true | Stripe Card | null | The card details to tokenize. |
metadata | false | Stripe Metadata | null | Basis Theory's token attributes used when creating the card token. |
Stripe Card
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
number | true | string | null | The card number without any separators |
exp_month | false | string | null | Two-digit string representing the card's expiration month |
exp_year | false | string | null | Four-digit string representing the card's expiration year |
cvc | false | string | null | Three or four-digit card verification code with automatic expiration |
name | false | string | null | The Cardholder Name |
Stripe Metadata
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
id | false | string | null | A value or expression specifying the token's ID. If not specified, a UUID will be assigned. |
mask | false | any | Depends on the token type | Token data mask. Can be an object, array, or any primitive type such as an integer, boolean, or string. See mask expressions. |
containers | false | array | Depends on the token type | Array of containers to place this token within. Each container is a path representing a logical grouping of tokens. See Token Containers for details. |
metadata | false | map<string, string> | null | A key-value map of strings containing non-sensitive data. |
search_indexes | false | array | null | Array of expressions used to generate indexes to be able to search against. |
fingerprint_expression | false | string | {{ data | stringify }} | Expressions used to fingerprint your token. |
deduplicate_token | false | bool | null | Whether the token is deduplicated on creation. |
expires_at | false | string | null | ISO8601 compatible Token expiration DateTime. See Token Expiration for more details. |
id of your token. See the documentation on Aliasing to learn more about best practices when specifying your own token ID.metadata of your token.Response
Returns a token if the token was created. Returns an error if there were validation errors, or the token failed to create.
{
"id": "c06d0789-0a38-40be-b7cc-c28a718f76f1",
"tenant_id": "77cb0024-123e-41a8-8ff8-a3d5a0fa8a08",
"type": "card",
"data": {
"number": "XXXXXXXXXXXX4242",
"expiration_month": "07",
"expiration_year": "2030",
"name": "John Doe"
},
"mask": {
"number": "{{ data.number | reveal_last: 4 }}",
"expiration_month": "{{ data.expiration_month }}",
"expiration_year": "{{ data.expiration_year }}",
"name": "{{ data.name }}"
},
"containers": ["/pci/high/"],
"metadata": {
"nonSensitiveField": "Non-Sensitive Value"
},
"search_indexes": ["{{ data.number | last4 }}"],
"fingerprint": "2W6iy15GJoqj5V696BnsadZVTst5fjJf24iW9Kg6hFDa",
"fingerprint_expression": "{{ data.number }}",
"created_by": "fb124bba-f90d-45f0-9a59-5edca27b3b4a",
"created_at": "2020-09-15T15:53:00+00:00",
"expires_at": "2030-08-26T19:23:57-07:00"
}
data and metadata attributes are redacted from the response when a token is deduplicated, unless the API Key has token:read permission in the targeted container. Click here to learn more about this behavior.Tokenize a Wallet DPAN
The same endpoint also accepts Apple Pay™ and Google Pay™ Device Primary Account Numbers (DPANs) forwarded from Stripe, so you can migrate stored wallet credentials off Stripe alongside your card volume using a single destination URL.
Instead of a card block, the request body carries wallet_type and a payment_method.network_token block. The endpoint creates the corresponding wallet resource — an Apple Pay™ resource or a Google Pay™ resource — with ingest_source set to stripe_forward.
Permissions
The application must hold token:create and the creator permission for the wallet being tokenized: apple-pay:create for wallet_type = "apple_pay", or google-pay:create for wallet_type = "google_pay".
token:create
apple-pay:create
google-pay:create
Choosing the wallet type
Inspect card.wallet.type on Stripe's Payment Method object to decide which shape to forward. Apple Pay™ payment methods are always DPAN, so the wallet.type value is sufficient. Google Pay™ payment methods can be either DPAN or FPAN — check card.wallet.google_pay.tokenization_type to distinguish.
Stripe card.wallet.type | tokenization_type | Forward as |
|---|---|---|
apple_pay | N/A — Apple Pay™ is always DPAN | Wallet DPAN with wallet_type = "apple_pay" |
google_pay | dpan_or_ecommerce_token (DPAN) | Wallet DPAN with wallet_type = "google_pay" |
google_pay | fpan (FPAN) | Card, no wallet_type |
| absent (non-wallet card) | — | Card, no wallet_type |
Google Pay™ FPANs forwarded as a card produce a regular Token of type card, not a Google Pay™ resource — an FPAN carries no wallet provenance and is indistinguishable from a non-wallet card on the network.
Request
- cURL
curl "https://api.basistheory.com/connections/stripe-forward/tokenize" \
-H "BT-API-KEY: <API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"wallet_type": "apple_pay",
"payment_method": {
"network_token": {
"number": "4111111111111111",
"cryptogram": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"exp_month": "12",
"exp_year": "2030",
"electronic_commerce_indicator": "07"
}
}
}'
Request Parameters
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
wallet_type | true | string | null | Identifies which wallet the DPAN belongs to. Must be exactly apple_pay or google_pay — case-sensitive, no surrounding whitespace. |
payment_method | true | Stripe Payment Method | null | The forwarded payment-method block containing the network token. |
A request must include exactly one of card or payment_method.network_token. Sending both, neither, or payment_method without a network_token returns 400.
Stripe Payment Method
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
network_token | true | Stripe Network Token | null | The wallet network token to tokenize. |
Stripe Network Token
| Attribute | Required | Type | Default | Description |
|---|---|---|---|---|
number | true | string | null | The DPAN. 13–19 digits, no separators. |
exp_month | true | string | null | Two-digit string representing the DPAN's expiration month (01–12). |
exp_year | true | string | null | Four-digit string representing the DPAN's expiration year. Must be in the future. |
cryptogram | false | string | null | The 3DS cryptogram from the wallet. Up to 64 characters. Single-use and not retained on the resource. |
electronic_commerce_indicator | false | string | null | Two-digit ECI value (e.g. 07). |
The DPAN cryptogram is single-use and cannot be replayed. It is not retained on the wallet resource and is never returned in any response or webhook payload.
Response
Returns the wallet resource that was created, with a 201 Created status. The response shape depends on wallet_type.
- Apple Pay™
- Google Pay™
{
"id": "c2995d93-600a-44a2-b6f1-2c25e46603a9",
"type": "dpan",
"tenant_id": "0def1587-e30b-44b7-ad3f-484b323a3917",
"status": "active",
"ingest_source": "stripe_forward",
"expires_at": "2030-12-31T00:00:00+00:00",
"created_by": "0a6475a5-4bb8-4165-8c31-7fbc058843bf",
"created_at": "2026-05-18T16:19:50.9013495+00:00",
"fingerprint": "7bAjvyqJqfPc4jRjniEk87vNrjR74Xax1HnMREWsTiMz",
"card": {
"bin": "411111",
"last4": "1111",
"expiration_month": 12,
"expiration_year": 2030,
"brand": "visa",
"funding": "credit"
},
"authentication": {
"threeds_cryptogram": null,
"eci_indicator": "07"
}
}
See the Apple Pay™ resource reference for the full attribute list. Wallet resources created through this endpoint always have type = "dpan" and ingest_source = "stripe_forward". threeds_cryptogram is always null on the response.
{
"id": "99978b31-78e8-4751-926c-83775bfb126b",
"type": "dpan",
"tenant_id": "eb210708-f709-495f-b4de-b9e3877bd4c3",
"status": "active",
"ingest_source": "stripe_forward",
"expires_at": "2030-12-31T00:00:00+00:00",
"created_by": "c1847894-2f72-4999-a47d-bf9a12d19ca9",
"created_at": "2026-05-18T16:19:50.9013495+00:00",
"fingerprint": "7bAjvyqJqfPc4jRjniEk87vNrjR74Xax1HnMREWsTiMz",
"card": {
"bin": "411111",
"last4": "1111",
"expiration_month": 12,
"expiration_year": 2030,
"brand": "visa",
"funding": "credit"
}
}
See the Google Pay™ resource reference for the full attribute list. Wallet resources created through this endpoint always have type = "dpan" and ingest_source = "stripe_forward". The Google Pay™ response does not include an authentication block.
The fingerprint is derived from the DPAN, so the same wallet credential tokenized through Stripe Forward and through POST /apple-pay or POST /google-pay will produce matching fingerprints — use it to detect duplicates across ingest paths.
Errors
| Status Code | Description |
|---|---|
400 | The request body is malformed — both card and payment_method.network_token present, neither present, wallet_type invalid, or network_token fields invalid. |
401 | Missing or invalid API key. |
403 | The application is missing the wallet-specific creator permission: apple-pay:create for wallet_type = "apple_pay", google-pay:create for wallet_type = "google_pay". |