> This page is for version v1 (default).
> For other versions, use one of these documentation indexes:
> - v1 (default): https://developers.truemed.com/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.truemed.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.truemed.com/_mcp/server.

# Create a subscription (new subscriber)

> Provision a payment token and take the first payment in a single flow.

For a customer subscribing and paying at the same time, one call does both: create a payment session
with `tokenize: true`, and Truemed charges the first order while provisioning a reusable token for the
ones after it.

The customer has to be present for this. Qualifying and entering an HSA/FSA card both happen on
Truemed's hosted pages, so there's no way to provision a token in the background.

## What you'll build

| What to build                           | API reference                                                                                                                  |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Create the session with tokenization on | [`POST /payments/v1/create_payment_session`](/api-reference/payment-sessions/create-payment-session)                           |
| Redirect the customer                   | Use the `redirect_url` from the response                                                                                       |
| Fulfill the first order                 | [`payment_session_complete`](/api-reference/payment-sessions/payment-session-completed-webhook) webhook, on `status: captured` |
| Store the token for later               | [`payment_token_updated`](/api-reference/payment-tokens/payment-token-updated-webhook) webhook                                 |

## Create the session

This is a normal `create_payment_session` call with `tokenize: true` added. All amounts are integers
in cents—`4200` is \$42.00.

```bash
curl https://api.truemed.com/payments/v1/create_payment_session \
  -X POST \
  -H "Content-Type: application/json" \
  -H "x-truemed-api-key: $TRUEMED_API_KEY" \
  -d '{
    "tokenize": true,
    "total_amount": 4200,
    "customer_name": "Alex Smith",
    "customer_email": "alex@example.com",
    "success_url": "https://example.com/subscribe/success?payment_session_id={{payment_session_id}}",
    "failure_url": "https://example.com/subscribe/failure?payment_session_id={{payment_session_id}}",
    "idempotency_key": "sub_12345_period_1",
    "metadata": "subscription_12345",
    "order_items": [
      {
        "name": "Monthly B12",
        "quantity": 1,
        "sku": "b12-monthly",
        "price": 4200,
        "amount_details": {}
      }
    ],
    "amount_details": {}
  }'
```

The response is the same as any payment session:

```json
{
  "id": "c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51",
  "redirect_url": "https://app.truemed.com/..."
}
```

Store the `id` against your subscription record. It identifies the first charge, and it comes back as
`payment_session_id` on the `payment_token_updated` webhook—that's how you match the token to the
right customer.

> **Note**
>
> `payment_token` and `tokenize` are mutually exclusive. Use `tokenize: true` to create a token; pass
> `payment_token` to charge an existing one. Sending both is rejected.

Send `metadata` if you want your own subscription or customer reference echoed back. It appears on
both webhooks.

## How the flow works

#### Create the session

Call `create_payment_session` with `tokenize: true` from your server.

#### Redirect the customer to Truemed

Send them to the `redirect_url`. They complete the health survey, see the items they're
subscribing to, and enter their HSA/FSA card.

#### Return the customer to your site

Truemed redirects to your `success_url`. Show an order-received or subscription-confirmed message.

#### Fulfill on payment_session_complete

Fulfill the first order when the webhook reports `status: captured`.

#### Store the token on payment_token_updated

Save the `payment_token` for future billing periods.

![New Subscriber Flow](/_fern-img/a865dc04431a6e1a49e2408fc061ba4f3e09295a0504b6aa45a53cc2a94d1821.webp)

## Redirect the customer

Send the customer to the `redirect_url` from the response. Truemed shows them the items they're
subscribing to, runs the health survey, and collects card details.

The customer is only sent to your `failure_url` if they click **Cancel** on Truemed's survey or
checkout pages. Everything else—including not qualifying—comes back through the webhooks.

> **Tip**
>
> Want customers to stay on your site instead of redirecting away? The same flow renders inline—there's
> no redirect at this step, and you receive a `postMessage` with the same destination instead. See
> [Embedded Checkout](/guides/setup/api/payment-sessions/i-frame-payment-session).

## Handle the two webhooks

Both fire after the customer is gone. They're independent, and one can arrive without the other.

| Event                      | When it fires                                                   | What to do                                                                                       |
| -------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `payment_session_complete` | Whenever the first charge changes status. Fires more than once. | Fulfill **only** on `status: captured`. On `rejected`, cancel the order and notify the customer. |
| `payment_token_updated`    | Once the token is provisioned and usable.                       | Store `payment_token` against the customer, matching on `payment_session_id`.                    |

Because you sent `tokenize: true`, the `payment_session_complete` payload carries the
`payment_token` alongside the payment result. Here's a `captured` event:

```json
{
  "payment_id": "c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51",
  "status": "captured",
  "created_at": "2026-08-10T09:30:00Z",
  "authorize_amount": 4200,
  "capture_amount": 4200,
  "captures": [
    { "id": "8f3c1b90-2d47-4a6e-9c11-5b7e0a2d3f48", "capture_amount": 4200, "captured_at": "2026-08-10T09:31:12Z" }
  ],
  "order_items": [
    {
      "item_id": "line_item_1",
      "name": "Monthly B12",
      "sku": "b12-monthly",
      "price": 4200,
      "quantity": 1,
      "total": 4200,
      "total_refundable": 4200,
      "quantity_refundable": 1
    }
  ],
  "metadata": "subscription_12345",
  "payment_token": "dev_tm_token_8a1f0c2e-77b4-4f0a-9c31-2f5c7a9d4e10"
}
```

A payment token is `{environment}_token_{uuid}`—`tm_token_...` in production and
`dev_tm_token_...` in sandbox. The `payment_token` field only appears when you created the session
with `tokenize: true` or passed an existing `payment_token`.

> **Warning**
>
> `payment_token` is present on **every** `payment_session_complete` event for a tokenized session, not
> just `captured`—including `processing`, before the LMN is verified. Seeing the field is not the same
> as the token being chargeable. Wait for `payment_token_updated`.

> **Warning**
>
> Do not create fulfillment requests, ship anything, or start a free trial until
> `payment_session_complete` reports `status: captured`. A customer reaching your `success_url` only
> means they finished checkout, not that the payment cleared.

The first charge follows the same status path as any Truemed payment, including the case where the
customer completes the survey but no LMN is issued—the session moves to `rejected` and you should tell
the customer the order won't be fulfilled. See
[Payment Lifecycle](/guides/setup/api/payment-sessions/payment-lifecycle) for every status, and
[Manual and Delayed Capture](/guides/setup/api/payment-sessions/manual-delayed-capture) if you
authorize now and capture later. The rule doesn't change in either case: no fulfillment before
`captured`.

> **Note**
>
> If no LMN is issued, `payment_token_updated` never fires—no token was provisioned. Don't hold the
> subscription open waiting for it.

## Free trials

To start a subscription with no charge up front, three fields are required **together**:

| Field                    | Value                                            |
| ------------------------ | ------------------------------------------------ |
| `total_amount`           | `0`                                              |
| `tokenize`               | `true`                                           |
| `display_next_bill_info` | `{ "total_amount": 4200, "date": "2026-09-10" }` |

Truemed shows the customer the next bill amount and date from `display_next_bill_info` when they enter
their payment details, so they know what they're agreeing to.

> **Note**
>
> `display_next_bill_info` is only valid when `total_amount` is `0`. Sending it with a non-zero amount is
> rejected, and a zero-amount session without it is rejected with a `400`—`Amount cannot be 0`.

A free trial still produces a `payment_session_complete` webhook, and you still wait for
`status: captured` before starting the trial—it confirms the payment method was stored successfully.

## Next steps

#### [Recurring Charges](/guides/setup/api/subscriptions/subsequent-billing-periods)

Charge the stored token on your billing schedule.

#### [Token Lifecycle](/guides/setup/api/subscriptions/token-lifecycle)

What to do when a charge fails and the customer has to act.