From 6fa718aedb188834d7477fc537182b093708d85d Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 29 Jul 2026 17:19:34 -0500 Subject: [PATCH 1/3] docs(subscriptions): POST /api/subscriptions/card-on-file ($0 setup-mode session) Documents the card-on-file endpoint that exposes the existing createCardOnFileSession helper over HTTP: a Stripe `mode: "setup"` checkout session that saves a payment method for the authenticated account without charging anything or starting a subscription. - adds the `/api/subscriptions/card-on-file` path to the accounts OpenAPI spec, mirroring `/api/subscriptions/sessions` and reusing its CreateSubscriptionSessionResponse and SubscriptionSessionErrorResponse schemas - adds a CreateCardOnFileSessionRequest schema: `successUrl` only, since the route resolves the account from the credentials and never accepts `accountId` from the caller - adds the frontmatter-only reference page and registers it in the Billing group of docs.json so it lands in llms.txt Part of recoupable/chat#1910 Co-Authored-By: Claude Fable 5 --- api-reference/openapi/accounts.json | 80 +++++++++++++++++++ .../subscriptions/card-on-file-create.mdx | 4 + docs.json | 1 + 3 files changed, 85 insertions(+) create mode 100644 api-reference/subscriptions/card-on-file-create.mdx diff --git a/api-reference/openapi/accounts.json b/api-reference/openapi/accounts.json index b8c50f33..a99db8af 100644 --- a/api-reference/openapi/accounts.json +++ b/api-reference/openapi/accounts.json @@ -790,6 +790,72 @@ } } }, + "/api/subscriptions/card-on-file": { + "post": { + "description": "Create a $0 card-on-file checkout session for the authenticated account. The session runs in Stripe `setup` mode: it saves a payment method against the account's Stripe customer without charging anything today and without starting a subscription. Use it so an account that later runs out of credits can be auto-recharged instead of dead-ending. The account is always resolved from the credentials; `accountId` is never accepted from the caller. Returns a hosted checkout URL that the client should redirect to.", + "security": [ + { + "apiKeyAuth": [] + }, + { + "bearerAuth": [] + } + ], + "requestBody": { + "description": "Card-on-file session parameters", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCardOnFileSessionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Card-on-file session created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateSubscriptionSessionResponse" + } + } + } + }, + "400": { + "description": "Bad request - missing or invalid parameters", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriptionSessionErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized - invalid or missing authentication", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriptionSessionErrorResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubscriptionSessionErrorResponse" + } + } + } + } + } + } + }, "/api/accounts/{id}/subscription": { "get": { "description": "Retrieve the subscription for an account, directly or via an organization.", @@ -4221,6 +4287,20 @@ } } }, + "CreateCardOnFileSessionRequest": { + "type": "object", + "required": [ + "successUrl" + ], + "properties": { + "successUrl": { + "type": "string", + "format": "uri", + "description": "The URL Stripe redirects to after the card is saved.", + "example": "https://chat.recoupable.dev?card=saved" + } + } + }, "CreateSubscriptionSessionResponse": { "type": "object", "required": [ diff --git a/api-reference/subscriptions/card-on-file-create.mdx b/api-reference/subscriptions/card-on-file-create.mdx new file mode 100644 index 00000000..8bc15beb --- /dev/null +++ b/api-reference/subscriptions/card-on-file-create.mdx @@ -0,0 +1,4 @@ +--- +title: 'Create Card On File Session' +openapi: '/api-reference/openapi/accounts.json POST /api/subscriptions/card-on-file' +--- diff --git a/docs.json b/docs.json index 46e11323..4fdaf992 100644 --- a/docs.json +++ b/docs.json @@ -334,6 +334,7 @@ "group": "Billing", "pages": [ "api-reference/subscriptions/sessions-create", + "api-reference/subscriptions/card-on-file-create", "api-reference/subscriptions/portal-create", "api-reference/accounts/subscription-get", "api-reference/credits/sessions-create", From a2bdb68127988440fe17bb6572d8bb35dcf3cd5b Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 29 Jul 2026 17:30:40 -0500 Subject: [PATCH 2/3] docs(subscriptions): payment-neutral wording on the shared checkout URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card-on-file endpoint reuses CreateSubscriptionSessionResponse, whose `url` said "complete the payment" — contradictory for a $0 setup session. Generalized to "complete Checkout", which is accurate for both endpoints. Co-Authored-By: Claude Fable 5 --- api-reference/openapi/accounts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-reference/openapi/accounts.json b/api-reference/openapi/accounts.json index a99db8af..76721b5e 100644 --- a/api-reference/openapi/accounts.json +++ b/api-reference/openapi/accounts.json @@ -4316,7 +4316,7 @@ "url": { "type": "string", "format": "uri", - "description": "The hosted checkout URL. Redirect to this URL to complete the payment.", + "description": "The hosted checkout URL. Redirect to this URL to complete Checkout.", "example": "https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6g7h8i9j0" } } From 3a95ee8b8177e931c4ef0d48f1e1f418d5c6deb5 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 29 Jul 2026 17:49:08 -0500 Subject: [PATCH 3/3] docs: tighten the card-on-file description and fix per-status error examples Review feedback: the endpoint description ran to five sentences. Cut to three, matching the terseness of the sibling subscriptions/sessions entry. Also add per-response examples for 400, 401 and 500. The shared error schema carries a single field-level example, so every status tab rendered the same 400-style message; the 401 tab claimed "successUrl is required" for what is actually an auth failure. Each example is now the string the route really returns, captured from the api preview. Co-Authored-By: Claude Fable 5 --- api-reference/openapi/accounts.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/api-reference/openapi/accounts.json b/api-reference/openapi/accounts.json index 76721b5e..d53ebae7 100644 --- a/api-reference/openapi/accounts.json +++ b/api-reference/openapi/accounts.json @@ -792,7 +792,7 @@ }, "/api/subscriptions/card-on-file": { "post": { - "description": "Create a $0 card-on-file checkout session for the authenticated account. The session runs in Stripe `setup` mode: it saves a payment method against the account's Stripe customer without charging anything today and without starting a subscription. Use it so an account that later runs out of credits can be auto-recharged instead of dead-ending. The account is always resolved from the credentials; `accountId` is never accepted from the caller. Returns a hosted checkout URL that the client should redirect to.", + "description": "Create a $0 card-on-file checkout session for the authenticated account. Stripe `setup` mode saves a payment method without charging anything or starting a subscription, so an account that later runs out of credits can be auto-recharged instead of dead-ending. Returns a hosted checkout URL that the client should redirect to.", "security": [ { "apiKeyAuth": [] @@ -829,6 +829,9 @@ "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionSessionErrorResponse" + }, + "example": { + "error": "successUrl must be a valid URL" } } } @@ -839,6 +842,9 @@ "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionSessionErrorResponse" + }, + "example": { + "error": "Exactly one of x-api-key or Authorization must be provided" } } } @@ -849,6 +855,9 @@ "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionSessionErrorResponse" + }, + "example": { + "error": "Internal server error" } } }