Create a subscription (new subscriber)

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 buildAPI reference
Create the session with tokenization onPOST /payments/v1/create_payment_session
Redirect the customerUse the redirect_url from the response
Fulfill the first orderpayment_session_complete webhook, on status: captured
Store the token for laterpayment_token_updated 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.

$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:

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

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.

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

1

Create the session

Call create_payment_session with tokenize: true from your server.

2

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.

3

Return the customer to your site

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

4

Fulfill on payment_session_complete

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

5

Store the token on payment_token_updated

Save the payment_token for future billing periods.

New Subscriber Flow
New Subscriber Flow

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.

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.

Handle the two webhooks

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

EventWhen it firesWhat to do
payment_session_completeWhenever 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_updatedOnce 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:

1{
2 "payment_id": "c51f5d44-9f8d-4e9f-bc5f-929a6f9f2f51",
3 "status": "captured",
4 "created_at": "2026-08-10T09:30:00Z",
5 "authorize_amount": 4200,
6 "capture_amount": 4200,
7 "captures": [
8 { "id": "8f3c1b90-2d47-4a6e-9c11-5b7e0a2d3f48", "capture_amount": 4200, "captured_at": "2026-08-10T09:31:12Z" }
9 ],
10 "order_items": [
11 {
12 "item_id": "line_item_1",
13 "name": "Monthly B12",
14 "sku": "b12-monthly",
15 "price": 4200,
16 "quantity": 1,
17 "total": 4200,
18 "total_refundable": 4200,
19 "quantity_refundable": 1
20 }
21 ],
22 "metadata": "subscription_12345",
23 "payment_token": "dev_tm_token_8a1f0c2e-77b4-4f0a-9c31-2f5c7a9d4e10"
24}

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.

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.

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 for every status, and Manual and Delayed Capture if you authorize now and capture later. The rule doesn’t change in either case: no fulfillment before captured.

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:

FieldValue
total_amount0
tokenizetrue
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.

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 400Amount 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