# List Balance Transactions Source: https://docs.withflex.com/api-reference/balance-transactions/list-balance-transactions /openapi.json get /v1/balance_transactions Returns a list of Balance transactions. # List captures Source: https://docs.withflex.com/api-reference/captures/list-captures /openapi.json get /v1/captures Returns a list of your captures. Optionally filter to a single checkout session with the `checkout_session` query parameter, and expand related objects (such as the payment intent) with `expand`. # Retrieve a capture Source: https://docs.withflex.com/api-reference/captures/retrieve-a-capture /openapi.json get /v1/captures/{capture_id} Retrieves the details of a capture. Use the `expand` query parameter to inline related objects (such as the payment intent). # Update a capture Source: https://docs.withflex.com/api-reference/captures/update-a-capture /openapi.json patch /v1/captures/{capture_id} Updates the specified capture by setting the values of the fields you pass; only `metadata` can be changed. Other capture fields, including captured amounts, cannot be modified. # Cancel Auth Checkout Session Source: https://docs.withflex.com/api-reference/checkout-sessions/cancel-auth-checkout-session /openapi.json post /v1/checkout/sessions/{id}/cancel Cancels the authorization on a Checkout Session's payment intents, releasing the held funds back to the customer. Only payment intents in a cancellable state are canceled; the request fails with a 422 if the session has no payment intents or none are cancellable. # Capture Checkout Session Source: https://docs.withflex.com/api-reference/checkout-sessions/capture-checkout-session /openapi.json post /v1/checkout/sessions/{id}/capture Captures funds previously authorized on a Checkout Session whose payment intents use the `manual` capture method. Capture the full authorized amount, or a partial amount by specifying `line_items` and component amounts (shipping, discount, tax). By default the remaining authorized amount stays available for further captures; set `final_capture` to `true` to release any uncaptured remainder. For split carts the capture is allocated across the eligible and non-eligible payment intents; use the capture allocation preview endpoint to inspect the split first. # Create Checkout Session Source: https://docs.withflex.com/api-reference/checkout-sessions/create-checkout-session /openapi.json post /v1/checkout/sessions Creates a Checkout Session. The response includes a hosted `redirect_url` where the customer completes payment, along with the computed totals and eligibility. Flex classifies the line items for HSA/FSA eligibility and, when a Letter of Medical Necessity is required, sets up the collection flow. The session's `mode` determines whether it results in a one-time payment, a subscription, an off-session charge against a saved payment method, or a setup-only flow. When the line items mix eligible and non-eligible products, a split cart is created so each portion can be paid with the appropriate payment method. # Get Checkout Session Source: https://docs.withflex.com/api-reference/checkout-sessions/get-checkout-session /openapi.json get /v1/checkout/sessions/{id} Retrieves the Checkout Session with the given ID. Use the `expand` parameters to inline related objects such as the customer, payment intent, or subscription in the response. # Get Line Items Source: https://docs.withflex.com/api-reference/checkout-sessions/get-line-items /openapi.json get /v1/checkout/sessions/{id}/line_items Returns the line items for the given Checkout Session. Each line item includes its price, quantity, and computed amounts. # List Checkout Sessions Source: https://docs.withflex.com/api-reference/checkout-sessions/list-checkout-sessions /openapi.json get /v1/checkout/sessions Returns a list of your Checkout Sessions, most recent first. # Preview Capture Allocation Source: https://docs.withflex.com/api-reference/checkout-sessions/preview-capture-allocation /openapi.json post /v1/checkout/sessions/{id}/capture_allocation Computes how a capture would be allocated across the Checkout Session's payment methods, without executing any capture. Takes the same request body as the capture endpoint and returns the per-payment-method allocations so you can preview a split-cart capture before committing. # Preview Refund Allocation Source: https://docs.withflex.com/api-reference/checkout-sessions/preview-refund-allocation /openapi.json post /v1/checkout/sessions/{id}/refund_allocation Computes how a refund would be allocated across the Checkout Session's payment methods, without executing any refund. Takes the same request body as the refund endpoint and returns the per-payment-method allocations so you can preview a split-cart refund before committing. # Reauth Checkout Session Source: https://docs.withflex.com/api-reference/checkout-sessions/reauth-checkout-session /openapi.json post /v1/checkout/sessions/{id}/reauth Re-authorizes the payment on a Checkout Session whose authorization has expired or been fully captured, creating a new payment intent so the funds can be captured again. Use `amount_override` to authorize a different amount than the session total; otherwise the remaining authorizable amount is used. For split carts, the eligible and non-eligible buckets are each re-authorized as needed. # Refund Checkout Session Source: https://docs.withflex.com/api-reference/checkout-sessions/refund-checkout-session /openapi.json post /v1/checkout/sessions/{id}/refund Refunds a captured Checkout Session, in whole or in part. Refund a total `amount`, specific `line_items`, or omit both to refund the full remaining amount; component amounts (tax, shipping, discount) can be supplied for accurate allocation. For split carts the refund is allocated across the eligible and non-eligible payment intents based on HSA/FSA eligibility; the explicit `payment_methods` allocation is gated behind a beta feature that must be enabled for your account. Use the refund allocation preview endpoint to inspect the split before refunding. A `checkout.session.refunded` event is emitted. # Resend Checkout Session Lomn Source: https://docs.withflex.com/api-reference/checkout-sessions/resend-checkout-session-lomn /openapi.json post /v1/checkout/sessions/{id}/lomn/resend Resends existing LOMN (Letter of Medical Necessity) emails for a checkout session. Looks up all consultations associated with the checkout session that have a letter of medical necessity and enqueues them to be emailed to the customer. # Resend Checkout Session Receipts Source: https://docs.withflex.com/api-reference/checkout-sessions/resend-checkout-session-receipts /openapi.json post /v1/checkout/sessions/{id}/receipts/resend Resends existing receipt emails for a checkout session. Looks up all receipts associated with the checkout session's payment intents and enqueues them to be emailed to the customer. Does not regenerate the PDFs. # Create Coupon Source: https://docs.withflex.com/api-reference/coupons/create-coupon /openapi.json post /v1/coupons Creates a coupon. Exactly one of `amount_off` or `percent_off` must be provided; supplying both, or neither, is rejected. When `duration` is `repeating`, `duration_in_months` is required. # Get Coupon Source: https://docs.withflex.com/api-reference/coupons/get-coupon /openapi.json get /v1/coupons/{id} Retrieves a coupon by its ID. # List Coupons Source: https://docs.withflex.com/api-reference/coupons/list-coupons /openapi.json get /v1/coupons Returns a list of your coupons. Optionally filter by `name`. # Create or update a customer Source: https://docs.withflex.com/api-reference/customers/create-or-update-a-customer /openapi.json post /v1/customers Creates a customer. This endpoint upserts by email: if a customer with the same email already exists, that customer is updated with the supplied values instead of a new one being created. A `customer.created` webhook event is fired only when a new customer is created (not on update). If `checkout_session_id` is provided, the referenced checkout session is updated to reference this customer. # Delete a customer Source: https://docs.withflex.com/api-reference/customers/delete-a-customer /openapi.json delete /v1/customers/{id} Deletes the specified customer. This is a soft delete: the customer is marked as deleted and no longer appears in list or retrieve responses, but historical records that reference the customer (such as payment intents and checkout sessions) continue to resolve it. # List a customer's payment methods Source: https://docs.withflex.com/api-reference/customers/list-a-customers-payment-methods /openapi.json get /v1/customers/{id}/payment_methods Returns a list of payment methods attached to the specified customer, sorted by ID in descending order (most recently created first). # List customers Source: https://docs.withflex.com/api-reference/customers/list-customers /openapi.json get /v1/customers Returns a list of your customers, sorted by ID in descending order (most recently created first). Deleted customers are excluded. # Retrieve a customer Source: https://docs.withflex.com/api-reference/customers/retrieve-a-customer /openapi.json get /v1/customers/{id} Retrieves the details of one of your customers. # Retrieve a customer's payment method Source: https://docs.withflex.com/api-reference/customers/retrieve-a-customers-payment-method /openapi.json get /v1/customers/{id}/payment_methods/{pm_id} Retrieves a single payment method by ID for the specified customer. # Update a customer Source: https://docs.withflex.com/api-reference/customers/update-a-customer /openapi.json patch /v1/customers/{id} Updates the specified customer. Only the fields you provide are changed; omitted fields are left unchanged. # Close Dispute Source: https://docs.withflex.com/api-reference/disputes/close-dispute /openapi.json post /v1/disputes/{dispute_id}/close Closes a dispute, accepting it as lost and forfeiting the disputed funds. Use this when you do not wish to contest the dispute. Closing is irreversible: it forfeits the disputed amount and the dispute fee, and no further evidence can be submitted. The dispute must be in a `needs_response` or `warning_needs_response` status. # Count Disputes Source: https://docs.withflex.com/api-reference/disputes/count-disputes /openapi.json get /v1/disputes/count Returns the number of disputes that currently require a response. Counts disputes in a `needs_response` or `warning_needs_response` status. Useful for surfacing a "disputes needing action" badge without paging through the full list. # Get Dispute Source: https://docs.withflex.com/api-reference/disputes/get-dispute /openapi.json get /v1/disputes/{dispute_id} Retrieves the details of an existing dispute. Retrieves a dispute by ID, including its evidence, evidence submission details, payment method details, and associated balance transactions. # List Disputes Source: https://docs.withflex.com/api-reference/disputes/list-disputes /openapi.json get /v1/disputes Returns a list of your disputes, most recent first. Evidence is omitted by default; pass `expand=evidence` to include the parsed evidence for each dispute. # Update Dispute Source: https://docs.withflex.com/api-reference/disputes/update-dispute /openapi.json post /v1/disputes/{dispute_id} Updates the evidence on a dispute, and optionally submits it to the card network. Saves the provided evidence fields, leaving any omitted fields unchanged; a file field sent as `""` is cleared. File fields reference Flex file IDs that must have purpose `dispute_evidence`; they are resolved to the underlying card-network files before submission. When `submit` is `true`, the evidence is finalized and submitted to the network to contest the dispute — evidence cannot be edited after submission. The dispute must be in a `needs_response` or `warning_needs_response` status. # Retry Event Source: https://docs.withflex.com/api-reference/events/retry-event /openapi.json post /v1/events/{id}/retry Redeliver an existing event to your webhook endpoints. The event is looked up by ID; a `404` is returned if no matching event exists. If a `webhook_id` is supplied in the body, the original event is redelivered to that specific webhook endpoint and the stored event is returned. If no body is supplied, the event is redelivered to all of your configured webhook endpoints; in this mode only `checkout.session.completed` events are supported — the checkout session is re-fetched with its related objects expanded and a new event (with a fresh `fevt_` id) is generated and delivered. # Expanding Resources Source: https://docs.withflex.com/api-reference/expanding-resources Learn how to request expanded responses with additional information about related objects. ## Expanding resources Many objects allow you to request additional information as an expanded response by using the expand query parameter. This parameter is available on all API requests, and applies to the response of that request only. You can expand responses in two ways. In many cases, an object contains the ID of a related object in its response properties. For example, a Checkout Session might have an associated Customer ID. You can expand these objects in line with the expand request parameter, in this specific example `expand_customer=true` would be the query parameter used. The expandable label in this documentation indicates ID fields that you can expand into objects. Some available fields aren't included in the responses by default. You can request these fields as an expanded response by using the expand request parameter. You can use the expand parameter on any endpoint that returns expandable fields. You can expand multiple objects at the same time by identifying multiple query params. ## Example use case This can be helpful for scenarios where I'd like to fetch multiple details of objects. For example, if I have a payment intent ID and want to: * Find the checkout session that payment intent corresponds to * Retrieve the customer object that belongs to the checkout session I can do all of the above by adding the following query params to the list checkout session request * **payment\_intent=fpi\_01HWBS24Z8EFZ7VHJTSEWF2DPQ** - Fetch the checkout session that belongs to this payment intent * **expand\_customer=true** - Expand the customer object ```bash theme={null} curl --request GET \ --url 'https://api.withflex.com/v1/checkout/sessions?payment_intent=fpi_01HWBS24Z8EFZ7VHJTSEWF2DPQ&expand_customer=true' \ --header 'authorization: Bearer fsk_test_YzNiYjdhNWItN2FkOS00ZTMyLWE5MmQtZTdhNzMzYzE2NTIy' \ --header 'content-type: application/json' \ --header 'user-agent: vscode-restclient' ``` And we get back the checkout session object with the customer object expanded: ```json theme={null} { "checkout_sessions": [ { "allow_promotion_codes": false, "amount_total": 7000, "amount_subtotal": 7000, "cancel_url": "https://withflex.com/thank-you?canceled=true", "capture_method": "automatic", "checkout_session_id": "fcs_01HWBRQCHTEVYWYT31N3QEGS9S", "created_at": 1714086982, "customer": { "customer_id": "fcus_01HWBRQPYZAG0P5S486AMZG18Y", "first_name": "Miguel", "last_name": "Toledo", "email": "miguel@hola.coml", "phone": "1234567890", "employer": null, "shipping": null, "created_at": "2024-04-25T23:16:32.866283Z" }, "customer_email": "miguel@hola.coml", "defaults": null, "expires_at": 1714090582, "invoice": null, "hsa_fsa_eligible": true, "letter_of_medical_necessity_required": true, "metadata": null, "mode": "subscription", "payment_intent": "fpi_01HWBS24Z8EFZ7VHJTSEWF2DPQ", "redirect_url": "http://localhost:3000/pay/c/fcs_01HWBRQCHTEVYWYT31N3QEGS9S", "setup_intent": null, "shipping_options": null, "shipping_address_collection": false, "shipping_details": null, "status": "open", "success_url": "https://withflex.com/thank-you?success=true", "subscription": "fsub_01HWBRRK1FT4QNG65KTJW8MH34", "tax_rate": null, "test_mode": false, "total_details": { "amount_discount": 0, "amount_tax": null, "amount_shipping": null }, "visit_type": "posture" } ] } ``` I could have also expanded the payment intent, invoice, or subscription, by following the pattern `expand_=true`. # Cancel Export Source: https://docs.withflex.com/api-reference/exports/cancel-export /openapi.json post /v1/exports/{id}/cancel Cancels an export, setting its `status` to `cancelled`. An export that has already `completed` cannot be cancelled; an already-cancelled export is returned unchanged. # Create Export Source: https://docs.withflex.com/api-reference/exports/create-export /openapi.json post /v1/exports Creates an export and starts generating the requested CSV file asynchronously. The export is returned with status `created` and is then processed in the background; poll it until its `status` is `completed` before requesting a download URL. # Create Export Url Source: https://docs.withflex.com/api-reference/exports/create-export-url /openapi.json post /v1/exports/{id}/presigned_url Generates a temporary, presigned URL for downloading the export's generated CSV file. The export must have a generated file available — typically once its `status` is `completed`. The returned URL expires after a short period, so request a fresh one when it lapses. # Get Export Source: https://docs.withflex.com/api-reference/exports/get-export /openapi.json get /v1/exports/{id} Retrieves the details of an existing export, including its current `status`. Supply the unique export ID returned when the export was created. # List Exports Source: https://docs.withflex.com/api-reference/exports/list-exports /openapi.json get /v1/exports Returns a list of your exports, sorted with the most recently created exports first. # Create File Source: https://docs.withflex.com/api-reference/files/create-file /openapi.json post /v1/files Uploads a file to Flex. The request must be a `multipart/form-data` upload containing a `file` part and a `purpose` part. The file must be a JPEG, PNG, or PDF no larger than 5 MB; PNGs using 16-bit depth or Adam7 interlacing are rejected. The uploaded file can then be referenced by ID elsewhere in the API, for example as evidence on a dispute. Requires the `files_write` scope. # List Files Source: https://docs.withflex.com/api-reference/files/list-files /openapi.json get /v1/files Returns a list of your files, sorted by creation date with the most recently created files first. Pass `purpose` to filter to a single file purpose. Requires the `files_read` scope. # Retrieve File Source: https://docs.withflex.com/api-reference/files/retrieve-file /openapi.json get /v1/files/{id} Retrieves the details of an existing file. Supply the unique file ID that was returned when the file was uploaded. Requires the `files_read` scope. # Introduction to the Flex API Source: https://docs.withflex.com/api-reference/introduction Learn the basics of integrating with the Flex API to accept HSA/FSA payments The Flex API allows you to integrate HSA/FSA payment capabilities directly into your application. This guide covers the basics of how to get started with the API, authentication, and core concepts. ## Authentication All API requests must include your API key in the Authorization header: ```bash theme={null} Authorization: Bearer YOUR_API_KEY ``` Always keep your API keys secure and never expose them in client-side code. You can generate and manage API keys in the [Partner Dashboard](https://dashboard.withflex.com/apikeys). ## Base URL All API requests should be made to the following base URL: ``` https://api.withflex.com/v1 ``` ## Content type API requests with a request body should specify the content type: ``` Content-Type: application/json ``` ## Test mode Flex provides a test mode environment that allows you to test your integration without processing real payments. Test mode API keys start with `fsk_test_`, while production API keys start with `fsk_`. Be sure to replace your test API keys with production keys when you're ready to go live. ## API response format All API responses are returned in JSON format. Successful responses will include the requested resource(s), while error responses will include an error code and message. Example successful response: ```json theme={null} { "checkout_session": { "checkout_session_id": "fcs_01HW5KTQQAAQ66WV19MFW4DTT4", "status": "complete", "amount_total": 4999, "redirect_url": "https://checkout.withflex.com/pay/c/fcs_01HW5KTQQAAQ66WV19MFW4DTT4", "mode": "payment" // ...other properties } } ``` Example error response: ```json theme={null} { "error": { "code": "invalid_request", "message": "Missing required parameter: price" } } ``` ## Rate limits The Flex API enforces rate limits to ensure stability. If you exceed the rate limit, you'll receive a `429 Too Many Requests` response. The response headers will include information about when you can retry. ## Core resources The Flex API is organized around these core resources: | Resource | Description | | :---------------- | :-------------------------------------------- | | Products | What you're selling (goods or services) | | Prices | How much and how often to charge for products | | Checkout Sessions | A session representing a purchase attempt | | Customers | The buyers of your products | | Payment Intents | The intent to make a payment | | Subscriptions | Recurring payment arrangements | ## Next steps Explore these guides to learn more about specific aspects of the Flex API : For a complete reference of all API endpoints, see the API Reference section. ## AI assistants (MCP) The Flex MCP exposes the API surface described here as typed tools for AI assistants — Claude, Cursor, Windsurf, and any other Model Context Protocol-compatible client. See the [Flex MCP overview](/developer-guides/integration/mcp/overview) to connect your assistant. # Get Invoice Source: https://docs.withflex.com/api-reference/invoices/get-invoice /openapi.json get /v1/invoices/{id} Get an invoice by id # List Invoices Source: https://docs.withflex.com/api-reference/invoices/list-invoices /openapi.json get /v1/invoices List invoices # Mark Invoice Uncollectible Source: https://docs.withflex.com/api-reference/invoices/mark-invoice-uncollectible /openapi.json post /v1/invoices/{id}/mark_uncollectible Mark an invoice as uncollectible. This will stop automatic collection attempts and move the associated subscription to an unpaid state. # Cancel Payment Intent Source: https://docs.withflex.com/api-reference/payment-intents/cancel-payment-intent /openapi.json post /v1/payment_intents/{id}/cancel Cancels a PaymentIntent # Get Payment Intent Source: https://docs.withflex.com/api-reference/payment-intents/get-payment-intent /openapi.json get /v1/payment_intents/{id} Retrieves the details of a PaymentIntent that has previously been created. # List Payment Intents Source: https://docs.withflex.com/api-reference/payment-intents/list-payment-intents /openapi.json get /v1/payment_intents Returns a list of payment intents # Send Receipt Source: https://docs.withflex.com/api-reference/payment-intents/send-receipt /openapi.json post /v1/payment_intents/{id}/receipt Send a receipt to customer for the associated payment # Create Payment Link Source: https://docs.withflex.com/api-reference/payment-links/create-payment-link /openapi.json post /v1/payment_links Creates a payment link: a shareable URL that generates a checkout session each time a customer opens it. The mode is determined by `line_items` — it becomes `subscription` if any price is recurring, otherwise `payment`; pass `mode=setup` to save a payment method without a charge (setup mode requires a `label` and empty `line_items`). Creating a link may create supporting objects as a side effect: inline `price_data` provisions a Product and Price (in Stripe and Flex), inline shipping rate data creates a Shipping Rate, and inline `coupon_data` discounts create Coupons. Promotion codes are not accepted as discounts here — use coupon IDs or inline coupon data. # Get Payment Link Source: https://docs.withflex.com/api-reference/payment-links/get-payment-link /openapi.json get /v1/payment_links/{id} Retrieves the details of an existing payment link, including its line items and applied discounts. If the link has no label yet, one is generated from its first line item's product and persisted before the link is returned. # List Payment Links Source: https://docs.withflex.com/api-reference/payment-links/list-payment-links /openapi.json get /v1/payment_links Returns a list of your payment links. Only active links are returned; archived (inactive) links are omitted. Results are ordered by ID descending. Supports cursor pagination via `starting_after`/`ending_before`; `offset` is deprecated. Line items and discounts are not expanded here — retrieve a single link to load them. # Update Payment Link Source: https://docs.withflex.com/api-reference/payment-links/update-payment-link /openapi.json patch /v1/payment_links/{id} Updates an existing payment link. Only the supplied fields are changed; omitted fields are left unchanged. The mutable fields are `label`, `after_completion`, and `active` — line items, discounts, and other parameters are fixed once the link is created. Set `active` to `false` to archive the link, which removes it from list results and shows visitors a deactivated page. # List Payouts Source: https://docs.withflex.com/api-reference/payouts/list-payouts /openapi.json get /v1/payouts Returns a list of payouts. # Create Price Source: https://docs.withflex.com/api-reference/prices/create-price /openapi.json post /v1/prices Creates a new price. Supply `product` to attach the price to an existing product, or `product_data` to create a new product inline in the same request. A price is one-time unless you include `recurring`, in which case it bills on the given interval. Amounts are in the smallest currency unit (cents) and charged in USD. If HSA/FSA eligibility is not set on the price, the parent product's eligibility applies. # Get Price Source: https://docs.withflex.com/api-reference/prices/get-price /openapi.json get /v1/prices/{id} Retrieves the price with the given ID. Pass `expand_product=true` to inline the full Product object instead of just its ID. Resolves your own prices, and (if you belong to an organization) prices owned by a sibling account. # List Prices Source: https://docs.withflex.com/api-reference/prices/list-prices /openapi.json get /v1/prices Returns a list of your prices, sorted by price ID in descending order (most recently created first). By default only active prices are returned; pass `active=false` to list archived ones, or filter to a single product with `product`. # Update Price Source: https://docs.withflex.com/api-reference/prices/update-price /openapi.json post /v1/prices/{id} Updates the specified price by setting the values of the parameters passed. Any parameters not provided are left unchanged. Only `description` and `active` can be updated; the unit amount, currency, and recurring interval of a price are immutable. Setting `active` to `false` archives the price so it can no longer be used for new purchases. # Create Product Source: https://docs.withflex.com/api-reference/products/create-product /openapi.json post /v1/products Creates a new product. Flex automatically determines the product's HSA/FSA eligibility from the supplied name, description, identifiers, and URL; if it can't be resolved synchronously, the product is created with eligibility pending and classification continues in the background, after which a `product.updated` event may fire. A `product.created` event is sent to your configured webhook endpoints on success. # Get Product Source: https://docs.withflex.com/api-reference/products/get-product /openapi.json get /v1/products/{id} Retrieves a product by ID. Resolves your own products, and (if you belong to an organization) products owned by a sibling account. # List Products Source: https://docs.withflex.com/api-reference/products/list-products /openapi.json get /v1/products Returns a list of your products, sorted by creation date with the most recently created products appearing first. By default only active products are returned; pass `active=false` to list archived ones. You can also filter by `client_reference_id` or by metadata using `metadata[key]=value` query parameters. # Update Product Source: https://docs.withflex.com/api-reference/products/update-product /openapi.json patch /v1/products/{id} Updates the specified product by setting the values of the parameters passed. Any parameters not provided will be left unchanged. Changing the name or description re-runs HSA/FSA eligibility determination, which may resolve asynchronously in the background. Passing `categories` replaces the product's existing categories (an empty array removes them all). A `product.updated` event is sent to your configured webhook endpoints on success. # Create Promo Code Source: https://docs.withflex.com/api-reference/promo-codes/create-promo-code /openapi.json post /v1/promo_codes Creates a promotion code referencing an existing coupon. If `code` is omitted, a unique code is generated automatically. The supplied `code` must be unique across your promotion codes. # Get Promo Code Source: https://docs.withflex.com/api-reference/promo-codes/get-promo-code /openapi.json get /v1/promo_codes/{id} Retrieves a promotion code by its ID. # List Promo Codes Source: https://docs.withflex.com/api-reference/promo-codes/list-promo-codes /openapi.json get /v1/promo_codes Returns a list of your promotion codes. Optionally filter by `active`. # Update Promo Code Source: https://docs.withflex.com/api-reference/promo-codes/update-promo-code /openapi.json post /v1/promo_codes/{id} Updates a promotion code by its ID. Only `active` and `metadata` can be modified; the referenced coupon and the code are immutable. # Create Refund Source: https://docs.withflex.com/api-reference/refunds/create-refund /openapi.json post /v1/refunds Creates a refund for a previously succeeded payment intent and returns the resulting Refund. The referenced payment intent must have a `succeeded` status. If `amount` is omitted, the full amount of the payment intent is refunded. A `refund.created` event is emitted, and the returned refund's `status` may still be `pending`; subsequent status changes are delivered via webhooks. # Get Refund Source: https://docs.withflex.com/api-reference/refunds/get-refund /openapi.json get /v1/refunds/{refund_id} Retrieves the details of an existing refund by its ID. # List Refunds Source: https://docs.withflex.com/api-reference/refunds/list-refunds /openapi.json get /v1/refunds Returns a list of your refunds, most recent first. # Update Refund Source: https://docs.withflex.com/api-reference/refunds/update-refund /openapi.json patch /v1/refunds/{refund_id} Updates the `reason` and/or `metadata` of an existing refund. Only these two fields can be changed; the refund amount, status, and associated payment cannot be modified after creation. Supplied metadata keys are merged into any existing metadata. # Get Review Source: https://docs.withflex.com/api-reference/reviews/get-review /openapi.json get /v1/reviews/{review_id} Retrieves the details of a fraud review. # List Reviews Source: https://docs.withflex.com/api-reference/reviews/list-reviews /openapi.json get /v1/reviews Returns a list of your fraud reviews, newest-first. Use the `open` filter to focus on reviews that still need action. # Create Setup Intent Source: https://docs.withflex.com/api-reference/setup-intents/create-setup-intent /openapi.json post /v1/setup_intents Creates a SetupIntent to collect a customer's payment method details for future off-session use. A new SetupIntent is created on each call and starts in the `requires_payment_method` status; it is not an upsert. The response includes a `client_secret` that you use to confirm the SetupIntent and attach a payment method from your frontend. # Get Setup Intent Source: https://docs.withflex.com/api-reference/setup-intents/get-setup-intent /openapi.json get /v1/setup_intents/{id} Retrieves the details of an existing SetupIntent. The response includes a fresh `client_secret` for confirming the SetupIntent from your frontend. Pass `expand=subscription` to inline the associated Subscription, when one exists. # List Setup Intents Source: https://docs.withflex.com/api-reference/setup-intents/list-setup-intents /openapi.json get /v1/setup_intents Returns a list of SetupIntents, sorted by ID in descending order (most recent first). The `client_secret` is not included on listed SetupIntents; retrieve a SetupIntent individually to obtain it. # Create Shipping Rate Source: https://docs.withflex.com/api-reference/shipping-rates/create-shipping-rate /openapi.json post /v1/shipping_rates Creates a new shipping rate. The rate is a fixed amount denominated in USD. New shipping rates are `active` by default. # Get Shipping Rate Source: https://docs.withflex.com/api-reference/shipping-rates/get-shipping-rate /openapi.json get /v1/shipping_rates/{id} Retrieves the shipping rate with the given ID. # List Shipping Rates Source: https://docs.withflex.com/api-reference/shipping-rates/list-shipping-rates /openapi.json get /v1/shipping_rates Returns a list of your shipping rates, most recent first. Pass `active` to return only active or only archived rates. # Update Shipping Rate Source: https://docs.withflex.com/api-reference/shipping-rates/update-shipping-rate /openapi.json put /v1/shipping_rates/{id} Updates the specified shipping rate. You can update `active` and `metadata`; both are overwritten with the values passed, and omitting `metadata` clears it. The shipping rate's `display_name` and `amount` are immutable. # Cancel Subscription Source: https://docs.withflex.com/api-reference/subscriptions/cancel-subscription /openapi.json delete /v1/subscriptions/{id} Cancels a customer's subscription. By default the subscription is canceled immediately; set `cancel_at_period_end` to `true` to keep it active until the end of the current billing period instead. Optionally issue a refund as part of the cancellation by setting `refund` to `full`, `prorated`, or `custom` (a `custom` refund requires a positive `refund_amount` in the smallest currency unit). Refunds are processed as a separate operation from the cancellation. The request body is optional; omitting it cancels immediately with no refund. # Get Refund Preview Source: https://docs.withflex.com/api-reference/subscriptions/get-refund-preview /openapi.json get /v1/subscriptions/{id}/refund_preview Previews the prorated refund that would result from canceling a subscription. Runs the same validation as a real prorated refund but issues no refund and creates no records, so it is safe to call repeatedly. Returns the prorated amount available to refund, the amount paid on the latest invoice, the current billing period, and the card that would be refunded. # Get Subscription Source: https://docs.withflex.com/api-reference/subscriptions/get-subscription /openapi.json get /v1/subscriptions/{id} Retrieves the subscription with the given ID. Use `expand` (or `expand_customer`) to inline related objects such as the customer, latest invoice, default payment method, or item prices. # List Subscriptions Source: https://docs.withflex.com/api-reference/subscriptions/list-subscriptions /openapi.json get /v1/subscriptions Returns a list of your subscriptions, most recent first. Optionally filter by customer or status. Use `expand` (or `expand_customer`) to inline related objects such as the customer, latest invoice, or item prices. # Resend Subscription Receipts Source: https://docs.withflex.com/api-reference/subscriptions/resend-subscription-receipts /openapi.json post /v1/subscriptions/{id}/receipts/resend Resends existing receipt emails for the initial checkout of a subscription. Looks up the receipts for the subscription's initial checkout-session payment and enqueues them to be emailed to the customer; the existing receipt PDFs are reused, not regenerated. # Update Subscription Source: https://docs.withflex.com/api-reference/subscriptions/update-subscription /openapi.json patch /v1/subscriptions/{id} Updates an existing subscription. Any omitted field is left unchanged, and an empty request body is a no-op that simply returns the current subscription. The change is first applied in Stripe and then mirrored to Flex, so the response reflects the resulting status and billing period. Updating `items` switches each item to the supplied recurring price (or to a newly created price when `price_data` is given, in which case a new Price is created in Stripe and Flex); item prices must be recurring. `proration_behavior` controls how mid-cycle changes are prorated (`create_prorations` by default). Supplied `metadata` is merged into any existing metadata. Passing an empty string for `coupon` removes the current coupon. # Flex Refund API Guide Source: https://docs.withflex.com/api-refund-best-practices **Important IDs to store:** * `checkout_session_id` - Extract from redirect URL (e.g., `fcs_01kbjcmbt5mhsmaggfqc1a4rns`) * This ID is required for creating refunds You can also retrieve the checkout session to get additional details: ```bash theme={null} GET /v1/checkout/sessions/{checkout_session_id} ``` Response includes: * `payment_intent_id` - Flex's internal payment ID * `latest_charge` - Charge ID * `status` - Should be `complete` for refunds *** ## Primary Refund Endpoint ### POST /v1/checkout/sessions//refund This is the **main refund endpoint** for processing refunds on completed checkout sessions. **Base URL:** `https://api.withflex.com` (or your custom domain) **Authentication:** ```text theme={null} Authorization: Bearer YOUR_API_KEY Content-Type: application/json ``` *** ## Request Format ### Option 1: Line Item-Based Refund (Granular Control) Refund specific products/prices with custom amounts: ```json theme={null} { "checkout_session": { "line_items": [ { "product": "fprod_01k3m6j3zqcvy6pbj1a65sszpw", "amount_to_refund": 2000 }, { "product": "fprod_01k3m6r0qj2bpc25q6h56r3jar", "amount_to_refund": 1500 } ], "amount_tax": 350, "amount_shipping": 500, "amount_discount": 200, "refund_metadata": { "OrderNo": "ORDER123", "Source": "Customer_Request", "Reason": "Damaged_Product" } } } ``` **Alternative: Use `price` instead of `product`** ```json theme={null} { "checkout_session": { "line_items": [ { "price": "fprice_01k3m6j3zqcvy6pbj1a65sszpw", "amount_to_refund": 2000 } ], "refund_metadata": { "reason": "customer_request" } } } ``` *** ### Option 2: Simple Amount-Based Refund Refund a specific total amount without line item breakdown: ```json theme={null} { "checkout_session": { "amount": 5000, "refund_metadata": { "reason": "customer_not_satisfied", "order_id": "12345" } } } ``` *** ### Option 3: Full Refund Refund the entire checkout session amount: ```json theme={null} { "checkout_session": { "refund_metadata": { "reason": "order_cancelled" } } } ``` Or simply: ```json theme={null} { "checkout_session": {} } ``` *** ## Request Parameters | Field | Type | Required | Description | | ------------------------------- | ------- | ------------- | ------------------------------------------------------------ | | `line_items` | array | No | Specific line items to refund with amounts | | `line_items[].product` | string | One of | Product ID to refund (e.g., `fprod_xxx`) | | `line_items[].price` | string | product/price | Price ID to refund (e.g., `fprice_xxx`) | | `line_items[].amount_to_refund` | integer | No | Amount in cents for this item (omit for full item refund) | | `amount` | integer | No | Total amount to refund in cents (alternative to line\_items) | | `amount_tax` | integer | No | Tax amount to refund in cents | | `amount_shipping` | integer | No | Shipping amount to refund in cents | | `amount_discount` | integer | No | Discount amount to refund in cents | | `refund_metadata` | object | No | Key-value pairs for tracking (order ID, reason, etc.) | *** ## Success Response **Status Code:** `200 OK` ```json theme={null} { "checkout_session": { "checkout_session_id": "fcs_01kbjcmbt5mhsmaggfqc1a4rns", "status": "complete", "amount_total": 10000, "amount_subtotal": 9000, "currency": "usd", "customer": { "customer_id": "fcus_xxx", "email": "customer@example.com", "name": "John Doe" }, "payment_intent": { "payment_intent_id": "fpi_xxx", "amount": 10000, "amount_received": 10000, "status": "succeeded" }, "line_items": [...], "refunds": [ { "refund_id": "fref_01kbjdxyz123", "payment_intent_id": "fpi_xxx", "amount": 3500, "status": "pending", "reason": null, "created_at": "2024-12-04T10:30:00Z", "test_mode": false, "metadata": { "OrderNo": "ORDER123", "Source": "Customer_Request" }, "reference_id": null, "reference_type": "acquirer_reference_number", "reference_status": "pending", "items": [ { "amount_refunded": 3500, "payment_intent": "fpi_xxx", "reference_id": null, "reference_type": "acquirer_reference_number", "reference_status": "pending" } ] } ] } } ``` **Key Response Fields:** * `refunds[].refund_id` - Store this for tracking refund status * `refunds[].status` - Current refund status (`pending`, `succeeded`, `failed`) * `refunds[].reference_id` - Acquirer Reference Number (ARN) for bank tracking (populated later) * `refunds[].reference_status` - Status of reference number (`pending`, `available`, `unavailable`) *** ## Error Responses ### Checkout Session Not Complete (422 Unprocessable Entity) ```json theme={null} { "error": { "type": "validation_error", "message": "Checkout session must be complete", "errors": [ { "loc": ["status"], "msg": "Checkout session must be complete", "type": "invalid_request_error" } ] } } ``` **Cause:** Checkout session status is not `complete` *** ### Checkout Session Not Found (404 Not Found) ```json theme={null} { "error": { "type": "not_found", "message": "checkout session does not exist" } } ``` **Cause:** Invalid `checkout_session_id` or doesn't belong to your account *** ### Unauthorized (401 Unauthorized) ```json theme={null} { "error": { "type": "unauthorized", "message": "Invalid API key" } } ``` **Cause:** Missing or invalid `Authorization` header *** ## Refund Statuses | Status | Description | | ----------------- | --------------------------------------------------------------- | | `pending` | Refund initiated, processing in progress | | `requires_action` | Additional action required (rare) | | `succeeded` | Refund completed, funds returned to customer | | `failed` | Refund failed (usually insufficient funds in connected account) | | `canceled` | Refund was canceled | **Typical Timeline:** * Refund status updates to `succeeded` within minutes to hours * Funds appear in customer's account within 5-10 business days (varies by bank) *** ## Reference Numbers for Tracking Flex provides **reference numbers** that can be used to track refunds with banks and payment networks. ### Reference Types | Type | Description | | ---------------------------- | ------------------------------------------------------- | | `acquirer_reference_number` | ARN - Most common, used for tracking with issuing banks | | `system_trace_audit_number` | Alternative tracking number for payment networks | | `retrieval_reference_number` | Used for dispute resolution and chargebacks | ### Reference Status | Status | Description | | ------------- | -------------------------------------------------------- | | `pending` | Reference number not yet available from payment network | | `available` | Reference number ready, can be provided to customer/bank | | `unavailable` | Payment network doesn't provide this reference type | **Note:** Reference IDs typically become available within hours after the refund is created. **Learn More:** [Stripe Reference Numbers Guide](https://support.stripe.com/questions/how-to-trace-a-refund-using-reference-numbers) *** ## Complete Workflow Examples ### Example 1: Full Refund **Step 1: Customer completes payment** You receive redirect after successful payment: ```text theme={null} https://yoursite.com/success?session_id=fcs_01kbjcmbt5mhsmaggfqc1a4rns ``` **Step 2: Customer requests full refund** ```bash theme={null} curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_01kbjcmbt5mhsmaggfqc1a4rns/refund \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "checkout_session": { "refund_metadata": { "reason": "customer_cancelled_order", "order_id": "ORD-12345", "requested_by": "customer_service" } } }' ``` **Step 3: Response** ```json theme={null} { "checkout_session": { "checkout_session_id": "fcs_01kbjcmbt5mhsmaggfqc1a4rns", "status": "complete", "amount_total": 15000, "refunds": [ { "refund_id": "fref_01kbjdxyz123", "amount": 15000, "status": "pending", "created_at": "2024-12-04T10:30:00Z", "metadata": { "reason": "customer_cancelled_order", "order_id": "ORD-12345" } } ] } } ``` **Step 4: Track refund status (optional)** ```bash theme={null} curl -X GET https://api.withflex.com/v1/refunds/fref_01kbjdxyz123 \ -H "Authorization: Bearer sk_live_xxx" ``` *** ### Example 2: Partial Refund by Line Items **Scenario:** Customer returns 1 of 3 items purchased **Request:** ```bash theme={null} curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_01kbjcmbt5mhsmaggfqc1a4rns/refund \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "checkout_session": { "line_items": [ { "product": "fprod_yoga_mat", "amount_to_refund": 3500 } ], "amount_tax": 280, "amount_shipping": 0, "refund_metadata": { "reason": "item_returned", "rma_number": "RMA-2024-1234", "returned_items": "Yoga Mat (Blue)" } } }' ``` **Explanation:** * Refunding only the yoga mat: \$35.00 * Including proportional tax: \$2.80 * Not refunding shipping (customer kept other items) * Total refund: \$37.80 *** ### Example 3: Partial Refund by Amount **Scenario:** Price adjustment without item-level detail **Request:** ```bash theme={null} curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_01kbjcmbt5mhsmaggfqc1a4rns/refund \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "checkout_session": { "amount": 2000, "refund_metadata": { "reason": "price_match_guarantee", "original_amount": 15000, "adjusted_amount": 13000, "competitor": "CompetitorStore" } } }' ``` **Explanation:** * Refunding \$20.00 difference due to price match * No line item breakdown needed * Metadata tracks the reason *** ### Example 4: Multiple Partial Refunds You can create multiple refunds for the same checkout session until the full amount is refunded. **First refund:** \$50 ```bash theme={null} curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_xxx/refund \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{"checkout_session": {"amount": 5000}}' ``` **Second refund:** \$30 ```bash theme={null} curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_xxx/refund \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{"checkout_session": {"amount": 3000}}' ``` **Important:** Track the total refunded amount to avoid over-refunding. *** ## Secondary Refund API ### Tracking and Listing Refunds After creating a refund via the checkout session endpoint, use the secondary refund API to track status: #### GET /v1/refunds/ ```bash theme={null} curl -X GET https://api.withflex.com/v1/refunds/fref_01kbjdxyz123 \ -H "Authorization: Bearer sk_live_xxx" ``` **Response:** ```json theme={null} { "refund": { "refund_id": "fref_01kbjdxyz123", "payment_intent_id": "fpi_xxx", "amount": 5000, "status": "succeeded", "reason": null, "created_at": "2024-12-04T10:30:00Z", "test_mode": false, "metadata": { "order_id": "12345" }, "reference_id": "ARN8493274932", "reference_type": "acquirer_reference_number", "reference_status": "available", "items": [...] } } ``` *** #### GET /v1/refunds (List Refunds) **Query Parameters:** * `payment_intent` - Filter by payment intent ID * `checkout_session` - Filter by checkout session ID * `status` - Filter by status **Examples:** ```bash theme={null} # All refunds curl -X GET https://api.withflex.com/v1/refunds \ -H "Authorization: Bearer sk_live_xxx" # Refunds for specific checkout session curl -X GET https://api.withflex.com/v1/refunds?checkout_session=fcs_01kbjcmbt5mhsmaggfqc1a4rns \ -H "Authorization: Bearer sk_live_xxx" # Succeeded refunds only curl -X GET https://api.withflex.com/v1/refunds?status=succeeded \ -H "Authorization: Bearer sk_live_xxx" ``` **Response:** ```json theme={null} { "refunds": [ { "refund_id": "fref_01kbjdxyz123", "payment_intent_id": "fpi_xxx", "amount": 5000, "status": "succeeded", "created_at": "2024-12-04T10:30:00Z", ... }, { "refund_id": "fref_01kbjdabc456", "payment_intent_id": "fpi_yyy", "amount": 3000, "status": "pending", "created_at": "2024-12-03T15:20:00Z", ... } ] } ``` *** #### PATCH /v1/refunds/ (Update Refund Metadata) Update refund metadata or reason (does not change amount or status): ```bash theme={null} curl -X PATCH https://api.withflex.com/v1/refunds/fref_01kbjdxyz123 \ -H "Authorization: Bearer sk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "refund": { "reason": "fraudulent", "metadata": { "updated_by": "admin", "investigation_id": "INV-2024-001" } } }' ``` *** ## Implementation Details ### Automatic Handling by Flex Flex automatically handles: ✅ **Reverse Transfer** - Funds are clawed back from the connected account (partner) ✅ **Application Fee Retention** - Flex keeps the platform fee (not refunded) ✅ **Webhook Processing** - Status updates via Stripe webhooks ✅ **Charge Tracking** - Records which charges were refunded in `refund_charge` table ✅ **Reference Number Retrieval** - ARN and other tracking numbers populated when available ### Partial Refund Behavior * Can refund any amount up to the original charge amount * Multiple partial refunds allowed until full amount refunded * Track `amount_received` vs total refunded to calculate remaining refundable amount ### Tax, Shipping, and Discount Refunds When refunding line items, you can specify component amounts: ```json theme={null} { "checkout_session": { "line_items": [ { "product": "fprod_xxx", "amount_to_refund": 5000 } ], "amount_tax": 400, // Proportional tax for refunded items "amount_shipping": 500, // Shipping refund "amount_discount": 250 // Discount adjustment } } ``` **Best Practice:** Calculate proportional tax when refunding partial line items. *** ## Best Practices ### 1. Store Checkout Session ID Always save the `checkout_session_id` after payment completion: ```javascript theme={null} // After payment redirect const urlParams = new URLSearchParams(window.location.search); const sessionId = urlParams.get('session_id'); // Store in your database await db.orders.update({ orderId: orderId, flexCheckoutSessionId: sessionId }); ``` *** ### 2. Verify Checkout Session Status Before attempting refund, confirm the session is complete: ```javascript theme={null} const session = await fetch( `https://api.withflex.com/v1/checkout/sessions/${sessionId}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ).then(r => r.json()); if (session.checkout_session.status !== 'complete') { throw new Error('Cannot refund incomplete checkout session'); } ``` *** ### 3. Use Metadata for Tracking Always include metadata to track refund context: ```json theme={null} { "checkout_session": { "amount": 5000, "refund_metadata": { "order_id": "ORD-12345", "reason": "customer_request", "requested_by": "customer_service_agent_42", "ticket_id": "TICKET-789", "timestamp": "2024-12-04T10:30:00Z" } } } ``` *** ### 4. Handle Refund Webhooks Subscribe to Flex webhooks for automatic refund status updates: **Event Types:** * `refund.created` - Refund initiated * `refund.succeeded` - Refund completed successfully * `refund.failed` - Refund failed **Webhook Payload Example:** ```json theme={null} { "type": "refund.succeeded", "data": { "object": { "refund_id": "fref_xxx", "payment_intent_id": "fpi_xxx", "amount": 5000, "status": "succeeded", "reference_id": "ARN123456789" } } } ``` *** ### 5. Track Remaining Refundable Amount For multiple partial refunds, track the remaining amount: ```javascript theme={null} const session = await getCheckoutSession(sessionId); const totalCharged = session.amount_total; const totalRefunded = session.refunds.reduce((sum, r) => sum + r.amount, 0); const remainingRefundable = totalCharged - totalRefunded; console.log(`Can still refund: $${remainingRefundable / 100}`); ``` *** ### 6. Save Reference Numbers Store ARNs for customer support inquiries: ```javascript theme={null} const refund = await getRefund(refundId); if (refund.reference_status === 'available') { await db.refunds.update({ refundId: refundId, arn: refund.reference_id, referenceType: refund.reference_type }); // Provide to customer for bank tracking console.log(`ARN: ${refund.reference_id}`); } ``` *** ### 7. Handle Errors Gracefully ```javascript theme={null} try { const response = await fetch(refundUrl, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify(refundRequest) }); if (!response.ok) { const error = await response.json(); if (error.error.type === 'validation_error') { console.error('Validation error:', error.error.message); // Handle validation errors } else if (response.status === 404) { console.error('Checkout session not found'); // Handle not found } else { console.error('Unexpected error:', error); // Handle other errors } } const result = await response.json(); console.log('Refund created:', result.checkout_session.refunds[0].refund_id); } catch (error) { console.error('Network error:', error); // Handle network errors } ``` *** ## Testing ### Test Mode Use test API keys (starting with `sk_test_`) to test refunds without affecting real money: ```bash theme={null} curl -X POST https://api.withflex.com/v1/checkout/sessions/fcs_test_xxx/refund \ -H "Authorization: Bearer sk_test_xxx" \ -H "Content-Type: application/json" \ -d '{"checkout_session": {}}' ``` **Test Mode Behavior:** * Refunds complete instantly with `status: "succeeded"` * No actual money movement * Reference IDs still generated for testing * Full webhook flow works ### Test Credentials For testing payments → refunds flow: **Email:** `miguel@homefit.com` **Password:** `password` Create test API keys at: `http://localhost:3001/partners/dashboard/developers` *** ## Troubleshooting ### Problem: "Checkout session must be complete" **Cause:** Trying to refund a checkout session that hasn't been paid or is in progress **Solution:** 1. Check session status: `GET /v1/checkout/sessions/{id}` 2. Ensure `status === "complete"` 3. Wait for payment to finish if still processing *** ### Problem: "checkout session does not exist" **Cause:** Invalid checkout session ID or doesn't belong to your account **Solution:** 1. Verify the `checkout_session_id` is correct 2. Ensure you're using the right API key (test vs. production) 3. Check that the session belongs to your partner account *** ### Problem: Refund shows "pending" for too long **Cause:** Stripe processing delay or connected account issues **Solution:** 1. Wait up to 24 hours for normal processing 2. Check Stripe dashboard for errors 3. Verify connected account has sufficient balance 4. Contact support if still pending after 24 hours *** ### Problem: "Failed to create refund" **Cause:** Stripe API error (usually insufficient funds in connected account) **Solution:** 1. Check connected account balance in Stripe dashboard 2. Ensure partner account has funds to cover refund 3. Review Stripe logs for specific error details 4. Contact Flex support with `checkout_session_id` *** ### Problem: Reference ID not available **Cause:** Payment network hasn't provided ARN yet **Solution:** 1. Reference IDs typically appear within hours 2. Check `reference_status` - if `pending`, wait 3. If `unavailable`, payment network doesn't provide this type 4. Poll `/v1/refunds/{refund_id}` periodically for updates *** ## API Endpoint Summary | Endpoint | Method | Purpose | | ----------------------------------- | ------ | ------------------------------------------------ | | `/v1/checkout/sessions/{id}/refund` | POST | **Primary** - Create refund for checkout session | | `/v1/refunds` | GET | List all refunds with filters | | `/v1/refunds/{refund_id}` | GET | Get single refund details | | `/v1/refunds/{refund_id}` | PATCH | Update refund metadata | | `/v1/checkout/sessions/{id}` | GET | Get checkout session details | *** ## Common Use Cases ### Use Case 1: Customer Service Full Refund ```javascript theme={null} async function processFullRefund(checkoutSessionId, reason) { const response = await fetch( `https://api.withflex.com/v1/checkout/sessions/${checkoutSessionId}/refund`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FLEX_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ checkout_session: { refund_metadata: { reason: reason, processed_by: 'customer_service', timestamp: new Date().toISOString() } } }) } ); const result = await response.json(); const refund = result.checkout_session.refunds[0]; console.log(`Refund created: ${refund.refund_id}`); return refund; } ``` *** ### Use Case 2: Return Processing with Line Items ```javascript theme={null} async function processReturn(checkoutSessionId, returnedItems) { // returnedItems = [{ productId, amount }, ...] const lineItems = returnedItems.map(item => ({ product: item.productId, amount_to_refund: item.amount })); const response = await fetch( `https://api.withflex.com/v1/checkout/sessions/${checkoutSessionId}/refund`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FLEX_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ checkout_session: { line_items: lineItems, amount_tax: calculateProportionalTax(returnedItems), refund_metadata: { reason: 'items_returned', rma_number: generateRmaNumber() } } }) } ); return await response.json(); } ``` *** ### Use Case 3: Price Adjustment ```javascript theme={null} async function applyPriceAdjustment(checkoutSessionId, adjustmentAmount, reason) { const response = await fetch( `https://api.withflex.com/v1/checkout/sessions/${checkoutSessionId}/refund`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.FLEX_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ checkout_session: { amount: adjustmentAmount, refund_metadata: { reason: 'price_adjustment', adjustment_type: reason, original_ticket: 'TICKET-123' } } }) } ); return await response.json(); } ``` *** ## Additional Resources * **Flex API Documentation:** Contact your Flex account manager * **Stripe Refund Guide:** [https://stripe.com/docs/refunds](https://stripe.com/docs/refunds) * **Reference Numbers:** [https://support.stripe.com/questions/how-to-trace-a-refund-using-reference-numbers](https://support.stripe.com/questions/how-to-trace-a-refund-using-reference-numbers) * **Webhooks Setup:** Contact Flex support for webhook configuration *** ## Support For issues or questions about refunds: 1. Check this guide for common solutions 2. Review Stripe dashboard for payment details 3. Contact Flex support with: * `checkout_session_id` * `refund_id` (if refund was created) * Error messages * Timestamp of the issue *** # Product Updates Source: https://docs.withflex.com/changelog Keep track of all the latest updates and improvements to Flex. Shopify Marketing App Rebuilt ## Shopify Marketing App, Rebuilt We rebuilt the Flex Shopify Marketing App to make it even easier to add HSA/FSA education to your storefront. New theme sections and blocks include product eligibility badges, announcement banners, FAQs, subscription messaging, and a "How to Checkout with Flex" walkthrough. These components help educate shoppers and improve conversion, all without custom development. Blocks are available directly in your Shopify Theme editor. [Shopify Marketing App →](https://apps.shopify.com/flex-marketing) Dashboard AI Assistant ## Dashboard AI Assistant You can now ask questions and get help directly in the Flex Dashboard with our new AI assistant. Look up orders and customers, ask questions about your account, and get answers to common HSA/FSA and integration topics without searching through documentation. Responses are grounded in your Flex account, providing answers specific to your business. Streamlined Onboarding ## Streamlined Onboarding The redesigned onboarding flow makes it easier to connect your store, configure payouts, and complete setup in one place. With a more streamlined experience, new merchants can get up and running faster, so they're ready to grow with HSA/FSA payments. ## Coupons on Subscriptions Coupons can now be applied to subscriptions through the Flex API. ## Subscription Trial End at Checkout Checkout sessions now support setting a `trial_end` when creating subscriptions, giving you more control over trial periods. ## Faster Invoice & Subscription Responses Optimized API responses when expanding invoice and subscription objects for faster response times. ## Invoice paid\_at Timestamp Invoices now expose a `paid_at` timestamp via the API for easier reconciliation. ## Shopify Order Reconciliation Transactions and payout reports now include the Shopify order number for faster reconciliation. ## Timezone-Sensitive Receipts Receipts now display the Date of Service and use the viewer's local timezone, keeping documentation accurate. Flex MCP Server ## Flex MCP Server Introducing the Flex MCP Server. You can now connect AI tools like Claude, Cursor, Windsurf, and VS Code to your Flex account and manage products, checkout sessions, disputes, subscriptions, and more using natural language. The server exposes the Flex API as a set of tools and supports both OAuth and API key authentication, allowing your team to complete common tasks without leaving the tools they already use. [MCP Docs →](/developer-guides/integration/mcp/overview) 05 26 Redesigned Dashboard Home ## Redesigned Dashboard Home The Flex Dashboard now opens to a redesigned homepage. You'll see recent activity, notifications, and product updates in a single view, while detailed reporting moves to a dedicated Analytics page. This provides a faster overview of your account while keeping reporting and analysis in a dedicated workspace. [Dashboard Home →](https://dashboard.withflex.com) 05 26 Consumer Portal Claim Submission ## Consumer Portal: Claim Submission & TPA-Specific Letters Customers can now submit HSA/FSA reimbursement claims directly from the Flex Consumer Portal. For orders that require a Letter of Medical Necessity, customers can select their administrator, complete the required documentation, and submit their claim in one place. This creates a self-service reimbursement experience and reduces documentation requests to your support team. This feature is rolling out to consumers throughout the week. [Learn More →](https://www.withflex.com/blog/letter-of-medical-necessity-problem-reimbursement) 05 26 Marketplace Redesign ## Marketplace Redesign Building on last month's Marketplace updates, we've introduced new designs, updated navigation, and faster browsing. Search now uses semantic matching, helping shoppers discover relevant HSA/FSA-eligible products even when they don't use exact keywords. We've also added new promotional placements that give brands more opportunities to reach high-intent shoppers. We're continuing to invest in the Marketplace experience, with more updates coming soon. [Marketplace →](https://www.withflex.com) 05 26 Custom Terms of Service at Checkout ## Custom Terms of Service at Checkout You can now add your own terms of service to the Flex checkout. Custom terms appear below the standard Flex Terms and Privacy notice, and can optionally require customer acceptance before payment. This is useful for merchants with their own purchase agreements, membership terms, or program requirements. ```json theme={null} { "checkout_session": { "custom_text": { "terms_of_service_acceptance": { "message": "I agree to Acme's program terms." }, "terms_of_service_checkbox_required": true } } } ``` ## Chat Support in the Flex Consumer Portal Customers can now get instant answers to common HSA/FSA, payment, receipt, and documentation questions directly in the consumer portal. ## Future Use Payment Links Payment Links now support `setup_future_use`, allowing cards to be saved for future off-session charges. ## Expandable Balance Transactions The `balance_transaction` is now expandable on Charge, Refund, Payout, and Dispute objects, with additional filtering support. ## Line Items Export Added a new Line Items export with line-item-level detail for a selected date range. ## More Precise API Errors Refund, product creation, and subscription endpoints now return clearer validation errors for invalid requests. *** Deprecation Notices ## Manual Payment-Intent Capture We're retiring the `POST /v1/payment_intents/:id/capture` endpoint in favor of `POST /v1/checkout/sessions/:id/capture`, which is now the standard capture path for Flex. The checkout session endpoint correctly handles split-cart allocations and partial captures. **Who this affects:** Merchants using the `/payment_intents/:id/capture` endpoint. All affected merchants have already been contacted directly. If you currently use this endpoint, switch to `POST /v1/checkout/sessions/:id/capture`. See the [Capture Checkout Session docs](https://docs.withflex.com/api-reference/checkout-sessions/capture-checkout-session) for more information. If you have questions, reach out to your Flex contact or email [support@withflex.com](mailto:support@withflex.com). **Timeline:** The endpoint will be removed at the end of June 2026. ## Top-Level Refunds We're retiring the `POST /v1/refunds` endpoint in favor of `POST /v1/checkout/sessions/:id/refund`, which is now the standard refund path for Flex. The checkout session endpoint correctly handles split-cart allocations and partial refunds. **Who this affects:** This endpoint is not currently used by merchant integrations. We're sharing this update as part of our API deprecation process. `POST /v1/refunds` will be migrated to `POST /v1/checkout/sessions/:id/refund`. See the [Refund Checkout Session docs](https://docs.withflex.com/api-reference/checkout-sessions/refund-checkout-session). If you have questions, reach out to your Flex contact or email [support@withflex.com](mailto:support@withflex.com). **Timeline:** The endpoint will be removed at the end of June 2026. 04 26 Dispute Management ## Dispute Management Dispute management is now available in the Flex Dashboard. Merchants can view, manage, and respond to disputes with full visibility into status and the ability to submit responses directly within Flex. This gives your team a single place to handle the full dispute lifecycle without relying on external systems. 04 26 Redesigned Marketplace ## Redesigned Marketplace & Homepage The Flex Marketplace is now the primary experience on [withflex.com](https://www.withflex.com). Visitors land directly in the Marketplace with improved search and product discovery, making it easier for HSA/FSA shoppers to find eligible brands and products. This helps drive more high-intent traffic to merchants using Flex. ## Checkout Performance Improved checkout performance across browsers, with the largest gains on Safari. ## Report Date Picker Navigation The report date picker now supports year navigation, making it easier to move across longer time ranges. ## Product Name Updates via API Product names can now be updated via the PATCH /v1/products endpoint. ## Organization Name Sync Updating your organization name in the dashboard now automatically updates the checkout page. 03 26 Consumer Portal ## Flex Consumer Portal Introducing the [Flex Consumer Portal](https://www.withflex.com/portal). Customers can now view their order history and access key documentation in one place. After signing in, they can explore past orders, review payment details, and download receipts and Letters of Medical Necessity directly. This gives customers a self-serve way to manage their purchases and reduces support requests. 03 26 Reports ## Expanded Reporting and Exports The Reports page has been redesigned to give you direct access to key data across your business. We've added new reports, including checkout sessions, subscriptions, products, line items, and customers. An improved export modal provides more context before export to reduce guesswork. Select your date range and export your data directly from the Flex dashboard. 03 26 Receipts ## Receipt Redesign Receipts have been upgraded with clearer formatting and more detailed information, including improved customer, line item, eligibility, payment, and refund details. Flex will also generate updated receipts when new charges or refunds occur, ensuring documentation stays accurate over time. This reduces support requests and makes it easier for customers to submit accurate receipts to their HSA/FSA administrators. 03 26 Recognized User ## Upgraded Recognized User Flow The checkout experience for returning customers with a valid Letter of Medical Necessity has been improved. Customers can now complete verification more quickly and continue to payment. These updates create a clearer, more streamlined experience for high-intent users and help improve conversion. 03 26 Resend Docs ## Resend Checkout Documentation Merchants can now resend checkout documentation, including receipts and Letters of Medical Necessity, directly from the dashboard. Customers can also access these documents through the consumer portal, but if needed, merchants can quickly reissue them. ## Subscription Coupons Coupons can now be applied to subscriptions across different intervals or limited to the first billing cycle. ## Custom Shipping Rate Name Checkout now displays your custom shipping rate names instead of a generic "Shipping" label. ## Off-Session Payment Failure Notifications Customers returning from a failed off-session payment now see a clear "card declined" message instead of a silent redirect. ## Date Filtering for Checkout Sessions API The checkout sessions API now supports date-based filtering, making it easier to query sessions within a specific range. ## Persistent Email Filters in Dashboard Email filters on the checkout sessions page now persist when navigating between list and detail views. ## 3DS Results on Charges 3DS authentication results are now recorded on charge objects, with improved handling for more reliable authentication. ## Refund Status Webhook Events Refund webhook events are now fully supported, enabling real-time tracking of refund status across its lifecycle. ## New Developer Documentation Added documentation for off-session checkouts, API product creation, refund workflows, and HSA/FSA test cards. 02 26 Multi-Category LMN ## Multi-Category LMN Support Flex can now support multi-category Letters of Medical Necessity during checkout. If your brand sells different types of products, such as skincare and supplements, customers can select multiple health conditions within a single session. When approved, multiple LMNs are provided to the customer within the same checkout flow, reducing friction and improving completion rates for mixed carts. 02 26 LMN Chat Performance ## Improved LMN Chat Performance We've optimized the performance of the Letter of Medical Necessity (LMN) chat interface to reduce latency and improve responsiveness. Customers now move through the consultation more smoothly, creating a better experience that helps improve completion rates and overall conversion. LMNs provided through Flex continue to be clinically reviewed. This update will be rolled out to merchants over the next few weeks. 02 26 Setup Mode ## Setup Mode Create checkout sessions in setup mode, allowing customers to save their HSA or FSA card without being charged immediately. This supports workflows like off-session billing, where the card can be charged later for one time payments or recurring purchases. The checkout experience will adapt to show a simplified card-saving flow. 02 26 Disputes ## Enhanced Dispute Management We've built full dispute handling into the Flex API. This includes visibility into dispute status, management capabilities, and the ability to submit responses directly through Flex. Merchants are able to manage disputes end-to-end without relying on external systems. 02 26 Checkout Session Actions ## Checkout Session Actions Manage checkout sessions directly from the Dashboard with a new actions dropdown. For sessions using manual capture, you can capture authorized payments, cancel uncaptured holds, or reauthorize expired payment intents, all without needing to contact support or use the API. ## Quick Create Price Added the 'Create Price' action to the quick create menu making it faster and easier to create a price from any page. ## Improved Payment Decline Messages Checkout now surfaces the more specific error message from the card issuer when an HSA/FSA payment is declined. ## Refund Page Improvements Added a max refund button and small UX improvements to the refund flow. ## HSA/FSA Card Detection We've enhanced how Flex identifies HSA/FSA cards using BIN lookup data. This update improves underlying payment classification and supports future functionality. ## Card Funding The funding field is now exposed on the Card API response. 01 26 Changelog 1 ## Checkout Session Expired Webhook Event You can now subscribe to the checkout.session.expired webhook event to get notified when a checkout session ends without a completed purchase. This lets you know when a customer drops off, enabling follow-up workflows like abandoned cart recovery or broader checkout performance analysis. 01 26 Changelog 2 ## Custom Fees for Shopify & WooCommerce Merchants using Shopify or WooCommerce can now include custom fees in Flex checkout sessions. These may include recycling fees, service charges, or other surcharges. Fees appear clearly in the checkout summary using the custom name provided. To use this feature, add an array of fees to the checkout\_session object when creating a session. ```json theme={null} { "checkout_session": { "fees": [ { "name": "Mattress Recycling Fee", "fee_type": "custom", "amount": 10000 } ] } } ``` 01 26 Changelog 3 ## Custom Checkout Text Now Supports Linking Custom checkout text now supports hyperlinks. Links open in a new tab to prevent customers from leaving the checkout flow. This is useful for linking to terms of service, return policies, HSA/FSA eligibility guides, or support pages, helping reduce customer confusion by providing easy access to important information during checkout. ```json theme={null} { "checkout_session": { "custom_text": { "submit": { "message": "Your custom message here (1-1200 characters) with a custom link." } } } } ``` 01 26 Changelog 4 ## Improved Create Price Modal The create price modal on the Payment Links page has been redesigned for better speed and usability. It now includes cleaner styling, a dedicated currency input, and more reliable form handling, making it easier to add prices when setting up new payment links. ## Consultation Fee Handling Improved how Flex handles consultation fees for customers who do not qualify for a Letter of Medical Necessity. ## Receipts for Subscription Trials Improved receipts to more accurately display one-time charges alongside free-trial subscription items when purchased together. ## Shipping Display Name Shipping options now support a custom display\_name field in webhooks and API responses for clearer identification. ## Split Payment Helper Text Updated checkout messaging to better explain split payment options and reimbursement steps when HSA/FSA funds are insufficient. ## Off-Session LMN Redirect Support Off-session charges that require a Letter of Medical Necessity now return a next\_action redirect instead of an error. ## Dashboard Pagination Improvements Improved pagination behavior across dashboard pages. 12 25 Changelog 1 ## Redesigned Flex Marketplace We’ve launched a fully redesigned Marketplace to improve how consumers discover HSA/FSA-eligible brands. With better navigation, enhanced search, and dedicated category pages, it’s now easier for shoppers to find what they need. The new structure also lays the foundation for product-level discovery, coming soon. These updates are designed to improve visibility, drive higher intent traffic, and help merchants grow their revenue. [Shop Now](https://www.withflex.com/shop). 12 25 Changelog 2 ## Custom Fees at Checkout Merchants can now apply custom fees directly to a checkout session, including recycling fees, service fees, or other surcharges. These fees are automatically distributed proportionally across eligible and ineligible items, ensuring accurate HSA/FSA handling in mixed carts. To use this feature, add an array of fees to the checkout\_session object when creating a session. ```json theme={null} { "checkout_session": { "fees": [ { "name": "Service Fee", "fee_type": "custom", "amount": 1000 } ] } } ``` 12 25 Changelog 3 ## Custom Checkout Text Merchants can now add custom text to select areas of the Flex checkout experience, giving you more control to inform your customers during checkout. This can be done via the API by including the custom\_text property when creating a checkout session. ```json theme={null} { "checkout_session": { "custom_text": { "submit": { "message": "Your custom message here (1-1200 characters)" } } } } ``` 12 25 Changelog 4 ## Subscription Payment Method Updates Merchants can now generate a shareable URL that allows customers to update their payment method on an active subscription. This improves flexibility, reduces churn from expired or invalid cards, and enables more robust subscription management through both the Flex Dashboard and API. Example API request: ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/subscriptions/{id}/update_payment_method_url \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" ``` 12 25 Changelog 5 ## Redesigned Notification Emails Flex notification emails have been migrated to Postmark’s template system to improve formatting, ensure consistent branding, and increase reliability across customer communications. This update applies to key touchpoints, including Letters of Medical Necessity, purchase receipts, and reimbursement notifications. 11 25 Changelog 1 ## Redesigned Dashboard Overview We rolled out a redesigned Dashboard Overview that makes your HSA/FSA channel easier to measure and optimize. The new experience brings together expanded metrics (like AOV, customer conversion, and returning customer rate), clearer performance insights, and a modern interface that highlights what’s driving growth. [Read more](https://www.withflex.com/blog/new-and-improved-dashboard-overview). 11 25 Changelog 2 ## New RevenueCat Integration Our new partnership with RevenueCat now enables compliant HSA/FSA payments directly inside your iOS, Android, and web apps. With just two setup steps, developers can unlock a new pre-tax revenue stream for eligible digital health subscriptions. [Read more](https://www.withflex.com/blog/flex-revenuecat-enabling-hsa-fsa-payments-for-in-app-subscriptions). 11 25 Changelog 3 ## Shopline Integration Flex is now integrated with SHOPLINE, making it easy for merchants to accept HSA/FSA payments directly at checkout. Setup takes only a few clicks, helping SHOPLINE’s 600,000+ merchants tap into consumers using their pre-tax health dollars. [Read more](https://www.withflex.com/blog/flex-shopline-powering-hsa-fsa-payments-for-shopline). Analytics ## Analytics The new Analytics tab in the dashboard gives you clear visibility into your HSA/FSA channel—so you can quickly see what’s working, spot drop-offs, and identify which customers and products drive the most value. Track key metrics like AOV, sales, conversion rate, and more. This is our first iteration of Analytics, and we’ll be expanding it with deeper insights in future releases. Payouts API ## Payouts API Access the same reconciliation details you see in payments exports — now directly via API for automated workflows. Order Summary ## Order Summary We’ve updated how order summaries appear at checkout, giving customers a clearer view of their purchase and boosting confidence before payment. Dashboard ARN and Client Reference ID Visibility ## Dashboard ARN and Client Reference ID Visibility You can now view both Acquirer Reference Numbers (ARNs) and Client Reference IDs directly in the dashboard. This makes it easier to track transactions end-to-end, resolve disputes quickly, and align with your internal systems. Checkout Session Filters & Reimbursements ## Checkout Session Filters & Reimbursements New statuses and filters make it easier to review past checkout sessions, including those that are reimbursements. Flex Secures $15M in Series A ## Flex Secures \$15M in Series A We’re excited to announce Flex’s \$15M Series A funding round led by First Round and Core VC, and Cameron Ventures with continued support from Rethink Impact, YC, and Liquid2. Since launching, we’ve been on a mission to modernize HSA/FSA payments. In that time, we’ve helped retailers like you unlock new revenue streams by making it simple for customers to spend their pre-tax health dollars directly at checkout. This funding allows us to expand our reach across the enterprise space, helping even more health and wellness brands grow—and making healthcare spending easier and more accessible for millions of Americans. Thank you for being part of our journey. We couldn’t do this without your partnership and trust. Expanded API Capabilities ## Expanded API Capabilities We added two new fields payment\_intents and amount\_received fields to checkout sessions. This enables you to see each checkout session’s associated payments, whether they were made via partial purchase or split cart, all in one API call. Line-Item Tax Support ## Line-Item Tax Support We now support tax at the line item level, enabling tax handling across different product categories. Improved WooCommerce Plugin Stability ## Improved WooCommerce Plugin Stability We resolved conflicts with other WordPress plugins, ensuring seamless compatibility with Flex. Shopify Subscription Support ## Shopify Subscription Support We now support Shopify subscriptions with HSA/FSA reimbursement. After a customer checks out, they’ll be prompted to complete a Letter of Medical Necessity post-purchase. If approved, they’ll receive the documents needed to submit a reimbursement to their provider. Dashboard tracking is coming soon to help you measure reimbursement success. Checkout Customizations ## Checkout Customizations You can now customize the Flex checkout to better match your brand. Add your logo, adjust the colors, and create a more consistent customer experience from start to finish. Improvements to Partial Purchase ## Improvements to Partial Purchase Your customers can now choose how much of their order to place on their HSA/FSA card. This improved flow helps maximize pre-tax benefit usage and reduces failed payments, leading to better conversion and a smoother checkout experience. Reports Page & Exports ## Reports Page & Exports You can now export key reports directly from the Flex dashboard — no custom requests or workarounds needed. Customize your date range before exporting to save time and avoid post-processing. This is just the beginning — more reports and analytics are on the way. RevenueCat integration ## RevenueCat integration Introducing the new Flex RevenueCat Integration. Connecting your subscription-based app to HSA/FSA payments used to require custom development and manual workarounds. Now, setup just takes a few clicks with no heavy lifting required. With the Flex and Revenue Cat integration it’s easy to offer compliant, tax-free payment options for eligible subscriptions, without disrupting your current flow. Reach out to your account manager to test the integration. Dashboard Navigation ## Dashboard Onboarding Getting started with Flex is now faster and easier. New merchants can access onboarding directly in the dashboard — from connecting payouts to inviting team members, everything you need to launch is now in one place. Don’t worry — if you still need help, Benny will be there to assist you. Shopify Marketing App ## Updated Documentation The Flex docs just got a major upgrade. We’ve added an AI assistant to help answer questions in real time, improved search to help you find the right article faster, introduced an interactive API reference so you can test endpoints directly from the page, and added a changelog so you can track what’s new as we continue to ship. WooCommerce Integration ## WooCommerce Introducing the new Flex WooCommerce Integration. Access to the \$150B in tax-free HSA/FSA funds on WooCommerce used to mean complex development, unclear documentation, and no support. Flex simplifies everything with a clean, minimal-dev integration that handles payments, compliance, and support so you don't have to. Reach out to Flex to get started with WooCommerce. Off-Session API ## Off-Session API Flex now supports off-session payments — the ability to charge a customer when they're not actively present in the checkout flow. This will enable Flex to create more automated, seamless payment experiences for merchants, while maintaining full compliance and reliability behind the scenes. With off-session payments, you can let shoppers skip re-entering payment info for a faster checkout experience. Dashboard Navigation ## Dashboard Navigation Flex dashboard updates are here — and it's just the beginning. We've refreshed the look, revamped navigation, and added a collapsible sidebar so you have more room to work and quicker access to the pages you use most. Stay tuned for more updates focused on making your workflows faster, smoother, and smarter. Shopify Marketing App ## Shopify Marketing App We're launching a new Flex Shopify Marketing app to help you drive awareness, educate customers, and increase conversion — all with just a few clicks. The app makes it easy to add assets like product badges and modals directly to your Shopify store. It's currently in review, and your account manager will follow up as soon as it's approved. Partial Purchase ## Partial Purchase We've launched Partial Purchase, a powerful new feature designed to improve conversion for merchants with higher-priced products or higher average order values. If an HSA/FSA card is declined due to insufficient funds, Flex will now charge the available balance on the HSA/FSA card and prompt the user to enter a credit or debit card to complete the purchase. Order + Subscription Page ## Order + Subscription Page The dashboard Order and Subscription pages have a new look. We've combined key data — including line items, customer info, payment details, and developer data — to give you a more holistic view of each checkout session. Filter By Email ## Filter By Email You can now filter by customer email on both the Order and Subscription Summary pages, making it easier to quickly locate the relevant record. A full suite of filters is in the works and coming soon. Refunds ## Refunds Custom refunds can now be issued directly from an Order page. A line item summary is shown so you can refund the correct amount from any individual item. For a quick option, select Max Refund to automatically apply the full amount. # Invite Users Source: https://docs.withflex.com/developer-guides/dashboard/invite-users Learn how to invite new members to your organization to access the partner dashboard and private documentation. Members of your organization will have access to the partner dashboard and private documentation. To grant access to new users, you can follow the steps outlined below to invite them to your organization. Go to the top left corner of the screen and locate your organization's name. Click on the organization name to open a dropdown menu. Organization dropdown menu in dashboard Look for the gear icon (⚙️) to access the settings. In the settings menu, find the 'Members' section. Click on 'Members' and you should see an option to invite people. Enter the email addresses of the users you'd like to invite and send the invitations. Members section in dashboard settings # Manage Your Customers Source: https://docs.withflex.com/developer-guides/dashboard/manage-customers Learn how to view and manage customer information through the Flex dashboard. Navigate to Customers to view a list of all your customers. You can export customer data from this view if necessary. Customers overview page in dashboard Click the three dots and select **View Customer** to see additional details about the customer, including all of their corresponding payments. A customer may not have payments if they only reached the contact section of checkout but did not enter their payment information. Customer details view in dashboard # Manage Your HSA/FSA Payments Source: https://docs.withflex.com/developer-guides/dashboard/manage-hsa-fsa-payments Learn how to view, export, and refund HSA/FSA payments through the Flex dashboard. Navigate to Payments to view all of the payments processed by Flex. You can export payment data from this view if necessary. Payments overview page in dashboard Click the three dots and select **View Payment** to see additional details about a payment, including the customer. Payment details view in dashboard If needed, you can refund a payment from this view. Refund option in payment details # Manage Your Products and Prices Source: https://docs.withflex.com/developer-guides/dashboard/manage-products-and-prices Learn how to view and manage your products and prices through the Flex dashboard. To create products and prices, follow the guide in the [Payment Link](/developer-guides/integration/payment-links/create) section. Navigate to Products to view your products and their eligibility at a glance. You can export product data from this view if necessary. Products overview page in dashboard Click the three dots and select **View Product** to see additional details about a product, including all of the corresponding prices. Product details view in dashboard # Get Insights Into HSA/FSA as a Channel Source: https://docs.withflex.com/developer-guides/dashboard/overview Understand HSA/FSA payment trends and performance through the Flex Dashboard. To manage HSA/FSA payments and view insights, visit the [Dashboard](https://dashboard.withflex.com/). Flex provides all the tools you need to understand HSA/FSA as a channel. Through the Dashboard, you can manage your HSA/FSA payments, products, customers, subscriptions, and payment links. You can also export data for easy financial reconciliation. If you're new to the Dashboard, you can watch a Loom walkthrough [here](https://www.loom.com/share/8d94a35a8b9540cca09e413e203bf64b). From the Overview page, adjust the date range to quickly see: * Revenue, conversion, and checkout trends over time * How this period compares to the previous period * Your highest-spending Flex customers * Your top-performing products on Flex * How revenue and checkout sessions break down by eligibility * How customers are completing purchases (direct payments vs reimbursements) Dashboard Overview 2 # Off Session Checkouts With a Product Requiring a Letter Source: https://docs.withflex.com/developer-guides/integration/checkout/example-walkthrough ## Introduction There are two key steps that must be taken in order for off session checkouts with a product requiring a Letter of Medical Necessity to work as intended: 1. Having the customer complete the initial (on session) checkout session 2. Using the customer's off-session-verified payment method (along with their `customer_id`) from step 1 in subsequent off session checkouts **Important:** For any products requiring a Letter of Medical Necessity, the customer **must** have an approved Letter for the product in order for any off session checkout sessions to work. They can receive this letter during the initial on session checkout discussed in the next section. If they are denied a letter or do not have one, off session checkouts will not work with that customer for the associated product(s). *** ## Initial (On Session) Checkout Session This step is vital to ensure all future off session payments work. During the initial on session checkout, the customer will: * Go through the checkout consultation to determine if they are eligible to receive a Letter of Medical Necessity for the product(s) in the checkout. * Provide a valid payment method that will then be verified for future off session payments (assuming the payment is successful). * Be assigned a Flex `customer_id` (if they are a customer using Flex for the first time; if they have used Flex prior they will already have this). All of these components are vital to ensure future off session payments for the customer work as expected. ### Example On Session Checkout Creation The key difference here compared to other checkouts is making sure you set the `setup_future_use` parameter to `off_session`. This is how Flex knows to validate the payment method used in the checkout for future off session payments. ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/checkout/sessions \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' \ --data '{ "checkout_session": { "line_items": [ { "price": "", "quantity": 1 } ], "setup_future_use": "off_session", "mode": "payment", "success_url": "https://example.com/success", "cancel_url": "https://example.com/cancel" } }' ``` This will create a checkout session that the customer will then go through to receive their letter and submit their initial payment. After the customer fills out their payment information and clicks "Pay Now" and the payment successfully goes through, the customer will have: * A valid Letter for the product (Flex will use this valid letter as part of the process when determining if future off session payments can go through) * A `customer_id` * A verified `payment_method` That is everything you will need for future off session payments. *** ## Off Session Checkout Creation Now that we have access to: * The customer's `customer_id` * The customer's off session verified `payment_method` * A validated Letter attached to the customer for the product We can move forward with off session payments. ### Getting customer\_id The `customer_id` is a required field for off session checkouts, and is also useful for programmatically finding the customer's payment method(s). There are several ways to obtain it: * Grabbing it from the `checkout.session.completed` webhook event that is fired when the checkout session completes * Using the associated checkout session's ID with the GET Checkout endpoint, and grabbing it from the returned checkout session object * Finding it with the List Customers endpoint * Finding it in the Customers tab of the Flex dashboard ### Getting payment\_method ID Now that we have the `customer_id`, we can use the List Payment Methods endpoint to grab the customer's payment method that is approved for off sessions. You will know if the payment method is viable for off sessions by seeing the `off_session` field with a value of `true` on the returned payment method object. ```json theme={null} { "payment_method_id": "", "billing_details": { ... }, "customer": "", "metadata": null, "card": { ... }, "created_at": "2026-01-23T19:13:46.145643Z", "test_mode": true, "off_session": true } ``` ### Example Off Session Checkout Now, to use all of this information to complete an off session payment, plug it into the correct places in the Off Session Checkout call: ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/checkout/sessions \ --header 'authorization: Bearer ' \ --header 'content-type: application/json' \ --data '{ "checkout_session": { "line_items": [ { "price": "", "quantity": 1 } ], "mode": "off_session", "customer": "", "payment_method": "", "success_url": "https://example.com/success", "cancel_url": "https://example.com/cancel" } }' ``` A successful call will return a response with the key details: ```json theme={null} { "status": "complete", "payment_intent": { "status": "succeeded", "amount": "", "amount_received": "" }, "letter_of_medical_necessity_required": true, "visit_type": "" } ``` If you get a response like the above, you have successfully set up an off session checkout flow! *** ## Additional Information ### What happens if the customer does not have a letter for the product? If you try to submit an off session payment in this scenario, the call will fail with an error response like this: ```json theme={null} { "detail": [ { "loc": ["visit_type"], "msg": "A LoMN is required for this visit type", "type": "invalid_request_error" } ] } ``` This indicates that the customer does not have a valid letter for the product in question, and they will not be able to have off session checkouts for the product until that letter is acquired. ### Do letters ever expire once a customer gets one? Yes, letters remain valid for **1 year** from the day the customer goes through the consultation. If you want to use off session payments for longer than that, customers will need to go through the on session flow once per year to maintain a valid letter on file. ### Will one letter cover a customer for all products? Not necessarily. Letters are tied to specific `visit_type` values, so a letter only applies to products that fall under the same `visit_type`. For example, if a customer went through an on session checkout and purchased a product bucketed under the `gym` visit type, they would then have a valid letter for all products in your catalog that also have a `gym` visit type. You could use an off session payment immediately for those products without the customer needing to go through the on session flow again. However, if they wanted to purchase a product under the `skincare` visit type, the `gym` letter would not be sufficient. The customer would need to go through another on session checkout consultation to receive a letter for `skincare` visit type products before off session payments would work. # Fulfill Orders Source: https://docs.withflex.com/developer-guides/integration/checkout/fulfillment Learn how to fulfill orders after a customer pays via checkout or payment links. After you integrate with Flex Checkout or create a Payment Link to take your customers to a payment form, you need a notification to fulfill their order after a successful payment. In this guide, you'll learn how to use webhooks to fulfill orders after a payment. ## Fulfillment guide Choose the webhook events that are relevant to your checkout session. * **customer.created** — When a customer record is created in Flex. * **payment\_intent.created** — As soon as the customer begins entering payment info. * **payment\_intent.succeeded** — After a successful payment (fires twice on split carts). * **customer.subscription.created** — For subscription sessions, when a subscription is first created. Includes the subscription's initial `status` (e.g., `trialing`, `active`, or `incomplete`). * **customer.subscription.updated** — Sent when an *existing* subscription changes (plan changes, payment failure, cancellation, etc.). **Not sent at subscription creation**, and not reliably sent on `trialing` → `active` transitions. Don't depend on this event for detecting new subscriptions. * **invoice.payment\_succeeded** — Sent when a subscription invoice is paid. The most reliable signal that a customer has paid — recommended for provisioning or extending membership / entitlements. * **checkout\_session.completed** — Sent after a checkout session has fully completed. **Recommended pattern for subscription fulfillment** To avoid missing new subscriptions, follow this pattern: * **Provision** membership / access on `customer.subscription.created`, gated on `status in (trialing, active)`. * **Confirm or extend** entitlement on `invoice.payment_succeeded` (covers initial payment and renewals). * **Sync state** (status changes, plan changes, payment failures) on `customer.subscription.updated`. * **Revoke** access on `customer.subscription.deleted`. Listening only on `customer.subscription.updated` will cause you to silently miss new subscriptions, since Stripe does not fire `.updated` at sub creation. Set up an HTTP endpoint to receive and log incoming events. For local testing, use ngrok or [webhook.site](https://webhook.site). ```js theme={null} // Using Express const app = require("express")(); const bodyParser = require("body-parser"); app.post( "/webhook", bodyParser.raw({ type: "application/json" }), (req, res) => { console.log("Got payload:", req.body); res.status(200).end(); } ); app.listen(4242, () => console.log("Running on port 4242")); ``` Register your handler's public URL in Flex. Here we listen for `checkout.session.completed`: ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/webhooks \ --header 'authorization: Bearer fsk_test_…' \ --header 'content-type: application/json' \ --data '{ "webhook": { "url": "https://withflex.com/handle_events", "events": ["checkout.session.completed"] } }' ``` Trigger a session to confirm your handler receives events: ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/checkout/sessions \ --header 'authorization: Bearer fsk_test_…' \ --header 'content-type: application/json' \ --data '{ "checkout_session": { "line_items": [{"price": "fprice_…","quantity": 1}], "success_url": "https://withflex.com/thank-you?success=true", "cancel_url": "https://withflex.com/thank-you?canceled=true", "mode": "payment" } }' ``` Then go through checkout: 1. Enter contact info 2. Fill any intake form 3. Use card number `4242 4242 4242 4242`, future expiry, any CVV, and postal code 90210 4. Click **Pay** You should see an event payload like: ```json theme={null} { "event_type": "checkout.session.completed", "object": { "checkout_session": { "status": "complete", "redirect_url": "https://checkout.withflex.com/pay/…", … } } } ``` Confirm the signature on incoming requests before processing. See [Verifying webhooks](/developer-guides/webhooks/verifying-webhooks). Handle `checkout.session.completed` to: * Save the order to your database * Send a receipt email * Update inventory via the Line Items API: ```bash theme={null} curl --request GET \ --url https://api.withflex.com/v1/checkout/sessions/fcs_…/line_items \ --header 'authorization: Bearer fsk_test_…' ``` Once deployed, register your production [webhook](/developer-guides/webhooks/overview) URL in Flex. # Define Your Checkout Experience Source: https://docs.withflex.com/developer-guides/integration/checkout/guides Learn how to customize checkout sessions for common use cases like shipping, taxes, discounts, and embedded checkout. Below we highlight common use cases that come up with checkout sessions and how to handle them. If there is a scenario that is not addressed, please reach out to [miguel@withflex.com](mailto:miguel@withflex.com) ## Shipping options If you need to ship a good, then you can have Flex collect shipping details for you by setting the `shipping_address_collection` parameter to `true` as part of the payload. If you additionally want to charge a shipping rate then you can do so by either: ### Creating a fixed shipping rate ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/shipping_rates \ --header 'authorization: Bearer fsk_test_YzNiYjdhNWItN2FkOS00ZTMyLWE5MmQtZTdhNzMzYzE2NTIy' \ --header 'content-type: application/json' \ --data '{"shipping_rate": {"display_name": "Standard Shipping","amount": 500}}' ``` Then specifying the returned `shipping_rate_id` as part of the payload when creating a checkout session. ```json theme={null} { "checkout_session": { "line_items": [ { "price": "fprice_01HW5NTAB88NK7HPD0H688EPG9", "quantity": 1 } ], "success_url": "https://withflex.com/thank-you?success=true", "mode": "payment", "cancel_url": "https://withflex.com/thank-you?canceled=true", "shipping_options": { "shipping_rate_id": "fshr_01HW6FC7ZYWNW4XFSE1KMD8JDW" } } } ``` ### Creating a dynamic shipping rate If your shipping is variable based off of the cart items then you can pass along the shipping data to create the shipping rate as part of the checkout session payload. ```json theme={null} { "checkout_session": { "line_items": [ { "price": "fprice_01HW5NTAB88NK7HPD0H688EPG9", "quantity": 1 } ], "success_url": "https://withflex.com/thank-you?success=true", "mode": "payment", "cancel_url": "https://withflex.com/thank-you?canceled=true", "shipping_options": { "shipping_rate_data": { "display_name": "Standard Shipping", "amount": 500 } } } } ``` ## Setting a tax amount If you need to pass along a tax amount to charge a customer you can do so by setting a tax\_rate. It is important to note that Flex does not currently calculate tax. This is an amount that you have previously calculated. ```json theme={null} { "checkout_session": { "line_items": [ { "price": "fprice_01HW5NTAB88NK7HPD0H688EPG9", "quantity": 1 } ], "success_url": "https://withflex.com/thank-you?success=true", "mode": "payment", "cancel_url": "https://withflex.com/thank-you?canceled=true", "tax_rate": { "amount": 599 } } } ``` ## Add a discount If you'd like to add a discount when charging a customer you can do so by creating a coupon then setting the discounts parameter as part of the payload. ### Creating a coupon ```bash theme={null} curl --request POST \ --url https://api.withflex.com/v1/coupons \ --header 'authorization: Bearer fsk_test_YzNiYjdhNWItN2FkOS00ZTMyLWE5MmQtZTdhNzMzYzE2NTIy' \ --header 'content-type: application/json' \ --data '{"coupon": {"name": "$10 off","amount_off": 1000}}' ``` Then using the returned coupon\_id as part of the checkout session payload ```json theme={null} { "checkout_session": { "line_items": [ { "price": "fprice_01HW5NTAB88NK7HPD0H688EPG9", "quantity": 1 } ], "success_url": "https://withflex.com/thank-you?success=true", "mode": "payment", "cancel_url": "https://withflex.com/thank-you?canceled=true", "discounts": { {"coupon": "fcoup_01HW6H666X6NMZ6NJHJ3SH3W93"} } } } ``` ## iFrame embedding The checkout may be embedded on a page or within a modal by placing the checkout within an iframe: ```html theme={null}