POST /api/v1/apps/:appUuid/tenants/:tenantUuid/payment-providers/:paymentProvider/checkout-sessions
Creates a new checkout sessions resource from a server-side app integration.
This endpoint documentation is generated from the current Fastify route implementation and should be treated as the implementation-level contract for this version of the API.
Source route file:
src/api/routes/external/private/apiKey/app/tenants/paymentProviders/checkout-sessions/post.ts
Request Method
POST
Base URL
https://api.userdocks.local:5000
Endpoint
/api/v1/apps/:appUuid/tenants/:tenantUuid/payment-providers/:paymentProvider/checkout-sessions
Path Variables
| Variable | Type | Required | Description |
|---|---|---|---|
appUuid | string | true | Path variable from route pattern. |
tenantUuid | string | true | Path variable from route pattern. |
paymentProvider | string | true | Use stripe or mollie. |
Query Parameters
No query parameters.
HTTP Headers
| Variable | Type | Required | Description |
|---|---|---|---|
x-api-key | string | true | API key value for the app. |
x-client-id | string | true | Must match :appUuid path variable. |
x-api-key-type | string | true | Use write. |
Content-Type | string | true | Use application/json for JSON request bodies. |
Request Body
Schema reference: createCheckoutSessionSchema, without tenantUuid. The
tenant is resolved from the :tenantUuid path variable.
{
"productUuid": "11111111-1111-1111-1111-111111111111",
"unitAmount": 1300,
"currency": "EUR",
"taxRateId": "txr_12345",
"quantity": 1,
"mode": "subscription",
"description": "Growth subscription",
"descriptionUnused": "Starter credit",
"recurringInterval": "month",
"recurringIntervalCount": 1,
"billingAddressCollection": true,
"shippingAddressCollection": false,
"successUrl": "https://app.example.com/success",
"cancelUrl": "https://app.example.com/cancel",
"termsOfServiceUrl": "https://app.example.com/terms",
"collectTermsOfServiceConsent": true,
"isTaxIdCollectionEnabled": true,
"isTaxIdCollectionRequired": false
}
description is optional. For Stripe checkout sessions in payment or setup
mode, it is copied to the generated Stripe invoice description (shown as the
invoice memo in Stripe). For Stripe subscriptions it becomes the recurring
invoice-line description. descriptionUnused is used only for a Stripe
upgrade's unused-time credit line. Both fields are limited to 500 characters.
Mollie ignores these Stripe-specific options. They do not change the response.
Successful Response
Success status code(s): 200.
{
"kind": "checkoutSessions",
"totalItems": 1,
"itemsLength": 1,
"items": [
{
"createdCheckoutSession": {
"id": 1,
"uuid": "csrow_11111111-1111-1111-1111-111111111111",
"appUuid": "app_11111111-1111-1111-1111-111111111111",
"paymentProviderUuid": "pp_11111111-1111-1111-1111-111111111111",
"sessionId": "cs_test_12345",
"nextActionUrl": "https://checkout.stripe.com/pay/cs_test_12345",
"userUuid": "user_11111111-1111-1111-1111-111111111111",
"tenantUuid": "tenant_11111111-1111-1111-1111-111111111111",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"deletedAt": null
},
"nextAction": {
"url": "https://checkout.stripe.com/pay/cs_test_12345"
}
}
]
}
Provider Notes
- Stripe and Mollie use the same endpoint shape.
unitAmountis a positive integer in the currency's minor unit:1300means EUR 13.00. Currencies are three-letter codes and are normalized to uppercase.- Inline subscriptions require
recurringInterval; the interval count defaults to1. - Legacy
productPriceUuidand providerpriceIdare deprecated but supported. items[0].nextAction.urlis the URL your app should open for the tenant.- The provider customer is created when the tenant does not already have one.
- A higher same-currency Stripe subscription price updates the existing item with an immediate proration invoice. The price applies only after payment.
- While authentication or retry is required,
nextAction.urlis the hosted invoice URL and the old plan remains active. Failed or expired pending updates create no Userdocks history. - Cross-currency Stripe price comparisons are rejected. Matching cadences keep their anchor; cadence changes use Stripe's normal reset behavior.
- Custom-description upgrades must keep the existing cadence. Their unused and
remaining proration lines retain Stripe's surrounding wording and dates, and
descriptionis reused on future billing-cycle invoice lines. Pending payment returns the hosted invoice, and retrying the identical request reuses it. - Renewals update the active plan period and do not create subscription history rows. See Subscription upgrades and plan history.
Error Responses
| HTTP Status | Example Error |
|---|---|
401 | {"errors":[{"validation":"error","code":"[E4010]","message":"Unauthorized Token or API key"}]} |
401 | {"errors":[{"validation":"error","code":"[E4011]","message":"Unauthorized API key type"}]} |
403 | {"errors":[{"validation":"error","code":"[E4030]","message":"App Is Disabled"}]} |
400 | {"errors":[{"validation":"error","code":"[E4000]","message":"Bad Request / validation error"}]} |
500 | {"errors":[{"validation":"error","code":"[E0000]","message":"Internal Server Error"}]} |
Example
const url = `https://api.userdocks.local:5000/api/v1/apps/appUuid-value/tenants/tenantUuid-value/payment-providers/stripe/checkout-sessions`;
const response = await fetch(url, {
method: 'POST',
headers: {
'x-api-key': '<api-key>',
'x-client-id': 'appUuid-value',
'x-api-key-type': 'write',
'Content-Type': 'application/json',
},
body: '{"productUuid":"11111111-1111-1111-1111-111111111111","unitAmount":1300,"currency":"EUR","taxRateId":"txr_12345","quantity":1,"mode":"payment","description":"September service invoice","billingAddressCollection":true,"shippingAddressCollection":false,"successUrl":"https://app.example.com/success","cancelUrl":"https://app.example.com/cancel","collectTermsOfServiceConsent":true}',
});
const data = await response.json();
console.log(response.status, data);
Named discounts
Supply the root-level discount with
{ "name": "Partner deal", "amount": 1000, "duration": "once" } or duration
"forever". Amounts use the price currency's minor units and reduce the product
line once, regardless of quantity. The original price and named reduction remain
visible on the invoice. Discounts must be smaller than the product line
subtotal; setup sessions and charges below the provider minimum are rejected.
See Named billing discounts for renewal behavior, per-line invoice examples, tax handling, and midterm upgrade calculations.