openapi: 3.0.3 info: title: 'ProxyOn API Documentation' description: 'ProxyOn provides a single REST API to manage subscribers, subscriptions, plans, entitlements, metered usage and invoices on top of Stripe. The subscriber portal is hosted by ProxyOn and the actual payment method is collected by Stripe. Authenticate with a project API key and call the v1 endpoints below.' version: 1.0.0 servers: - url: 'https://proxyon.teknoza.be' tags: - name: Subscribers description: 'Manage subscribers in your project' - name: Subscriptions description: 'Subscription lifecycle and listing' - name: Plans description: 'List and retrieve plans in your project' - name: Portal description: 'Whitelabel billing portal sessions' - name: Entitlements description: 'Resolve subscriber feature entitlements' - name: Usage description: 'Metered usage records and summaries' - name: Invoices description: 'List and retrieve invoices' - name: 'Webhook Endpoints' description: 'Manage outbound webhook endpoints for your project' components: securitySchemes: default: type: http scheme: bearer description: 'Send the project API key as Bearer pxn_test_… or Bearer pxn_live_…, or use the X-Project-Api-Key header with the same value.' security: - default: [] paths: /api/v1/subscribers: get: summary: 'List subscribers' operationId: listSubscribers description: "Returns the most recent subscribers in your project (up to 100). Use the\n`email` filter to look one up by address, or `type` to narrow the list to\nusers or organizations. Cursor pagination is not exposed yet — pivot\nbootstrap migrations should rely on these filters." parameters: - in: query name: type description: 'Filter by subscriber type ("user" or "organization").' example: organization required: false schema: type: string description: 'Filter by subscriber type ("user" or "organization").' example: organization - in: query name: email description: 'Filter by exact email address.' example: user@example.com required: false schema: type: string description: 'Filter by exact email address.' example: user@example.com responses: 200: description: 'List wrapper. `data[]` items follow the Subscriber resource shape.' content: application/json: schema: type: object example: object: list data: - object: subscriber id: sbr_01HXYZ external_id: user_12345 type: user email: user@example.com name: 'John Doe' metadata: [] created_at: '2026-05-20T10:00:00+00:00' updated_at: '2026-05-20T10:00:00+00:00' properties: object: type: string example: list data: type: array example: - object: subscriber id: sbr_01HXYZ external_id: user_12345 type: user email: user@example.com name: 'John Doe' metadata: [] created_at: '2026-05-20T10:00:00+00:00' updated_at: '2026-05-20T10:00:00+00:00' items: type: object properties: object: type: string example: subscriber id: type: string example: sbr_01HXYZ external_id: type: string example: user_12345 type: type: string example: user email: type: string example: user@example.com name: type: string example: 'John Doe' metadata: type: array example: [] created_at: type: string example: '2026-05-20T10:00:00+00:00' updated_at: type: string example: '2026-05-20T10:00:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscribers:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscribers:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscribers:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' tags: - Subscribers post: summary: 'Create or update a subscriber' operationId: createOrUpdateASubscriber description: "Idempotently upserts a subscriber by `external_id`. Call this right after\na customer signs up in your application — Proxyon will create the\nsubscriber on Stripe if needed and mirror the identity locally. Pass an\n`Idempotency-Key` header to safely retry on network errors." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: subscriber id: sbr_vj45sybaji6zmnhaeugu external_id: usr_yqoelvt3yv0r type: user email: okon.justina@example.com name: 'Misael Runte' metadata: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: subscriber id: type: string example: sbr_vj45sybaji6zmnhaeugu external_id: type: string example: usr_yqoelvt3yv0r type: type: string example: user email: type: string example: okon.justina@example.com name: type: string example: 'Misael Runte' metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscribers:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscribers:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscribers:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' tags: - Subscribers requestBody: required: true content: application/json: schema: type: object properties: external_id: type: string description: 'Unique external identifier for the subscriber.' example: user_12345 type: type: string description: 'Subscriber type: "user" or "organization".' example: user email: type: string description: 'Email address of the subscriber.' example: user@example.com name: type: string description: 'Display name of the subscriber.' example: 'John Doe' metadata: type: object description: 'Arbitrary key-value metadata.' example: plan: premium properties: { } required: - external_id - type '/api/v1/subscribers/{external_id}': get: summary: 'Retrieve a subscriber' operationId: retrieveASubscriber description: "Returns the subscriber identified by your application's `external_id`.\nUseful to verify Proxyon has the latest profile information or to fetch\nthe linked Stripe customer id." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: object: subscriber id: sbr_cidicwud531rtdomve7u external_id: usr_cixbojokeli6 type: user email: price.amber@example.org name: 'Miss Jazlyn Keebler III' metadata: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: subscriber id: type: string example: sbr_cidicwud531rtdomve7u external_id: type: string example: usr_cixbojokeli6 type: type: string example: user email: type: string example: price.amber@example.org name: type: string example: 'Miss Jazlyn Keebler III' metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscribers:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscribers:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscribers:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Subscribers patch: summary: 'Update a subscriber' operationId: updateASubscriber description: "Partially updates a subscriber. Only the fields you send are changed;\nexisting email, type, name and metadata are preserved. Stripe is\nreconciled automatically." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: subscriber id: sbr_xufe7szcgyq78ebjuql2 external_id: usr_xiwkagq7cvll type: user email: lafayette.considine@example.com name: 'Mr. Adriel Romaguera' metadata: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: subscriber id: type: string example: sbr_xufe7szcgyq78ebjuql2 external_id: type: string example: usr_xiwkagq7cvll type: type: string example: user email: type: string example: lafayette.considine@example.com name: type: string example: 'Mr. Adriel Romaguera' metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscribers:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscribers:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscribers:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Subscribers requestBody: required: false content: application/json: schema: type: object properties: type: type: string description: 'Subscriber type: "user" or "organization".' example: organization email: type: string description: 'New email address.' example: newemail@example.com name: type: string description: 'New display name.' example: 'Jane Doe' metadata: type: object description: 'Updated metadata (merge).' example: plan: basic properties: { } delete: summary: 'Delete a subscriber' operationId: deleteASubscriber description: "Soft-deletes a subscriber. Active subscriptions are cancelled at the\nperiod end by Stripe; the local row is kept for historic invoice access." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 204: description: 'Subscriber soft-deleted.' content: application/json: schema: type: object nullable: true 403: description: 'Forbidden — API key lacks the required scope `subscribers:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscribers:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscribers:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Subscribers parameters: - in: path name: external_id description: 'The external ID of the subscriber.' example: architecto required: true schema: type: string '/api/v1/subscribers/{external_id}/subscriptions': get: summary: "List a subscriber's subscriptions" operationId: listASubscribersSubscriptions description: "Returns every subscription for the subscriber, active ones first. Use\nthis to display a billing history page or to find the subscription id\nto cancel, swap or resume." parameters: [] responses: 200: description: "List of the subscriber's subscriptions, active subscriptions first. `data[]` items follow the Subscription resource shape." content: application/json: schema: type: object example: object: list data: - object: subscription id: sub_01HXYZ project_id: '1' subscriber_id: '1' plan_id: '1' status: active current_period_start: '2026-05-01T00:00:00+00:00' current_period_end: '2026-06-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: [] created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' properties: object: type: string example: list data: type: array example: - object: subscription id: sub_01HXYZ project_id: '1' subscriber_id: '1' plan_id: '1' status: active current_period_start: '2026-05-01T00:00:00+00:00' current_period_end: '2026-06-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: [] created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' items: type: object properties: object: type: string example: subscription id: type: string example: sub_01HXYZ project_id: type: string example: '1' subscriber_id: type: string example: '1' plan_id: type: string example: '1' status: type: string example: active current_period_start: type: string example: '2026-05-01T00:00:00+00:00' current_period_end: type: string example: '2026-06-01T00:00:00+00:00' trial_ends_at: type: string example: null nullable: true cancel_at: type: string example: null nullable: true canceled_at: type: string example: null nullable: true metadata: type: array example: [] created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-01T00:00:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscriptions:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscriptions:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscriptions:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Subscriptions parameters: - in: path name: external_id description: 'External identifier of the subscriber.' example: user_12345 required: true schema: type: string '/api/v1/subscriptions/{id}': get: summary: 'Retrieve a subscription' operationId: retrieveASubscription description: "Returns the subscription identified by its public id (`sub_*`) or numeric\nid. Includes the resolved plan, prices and current billing period bounds." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: object: subscription id: sub_k8hhj4w6x1i0zqbbtjcz project_id: '29' subscriber_id: '11' plan_id: '12' status: active current_period_start: '2026-06-01T00:00:00+00:00' current_period_end: '2026-07-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: subscription id: type: string example: sub_k8hhj4w6x1i0zqbbtjcz project_id: type: string example: '29' subscriber_id: type: string example: '11' plan_id: type: string example: '12' status: type: string example: active current_period_start: type: string example: '2026-06-01T00:00:00+00:00' current_period_end: type: string example: '2026-07-01T00:00:00+00:00' trial_ends_at: type: string example: null nullable: true cancel_at: type: string example: null nullable: true canceled_at: type: string example: null nullable: true metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscriptions:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscriptions:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscriptions:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Subscriptions parameters: - in: path name: id description: 'Subscription public id (sub_*) or numeric id.' example: sub_01JXY... required: true schema: type: string '/api/v1/subscriptions/{id}/cancel': post: summary: 'Cancel a subscription' operationId: cancelASubscription description: "Cancels a subscription. By default it stays active until the end of the\ncurrent billing period; pass `at_period_end=false` to cancel immediately\nand prorate the unused time." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: subscription id: sub_kdp9smztvtetz6ztemnl project_id: '39' subscriber_id: '15' plan_id: '15' status: active current_period_start: '2026-06-01T00:00:00+00:00' current_period_end: '2026-07-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: subscription id: type: string example: sub_kdp9smztvtetz6ztemnl project_id: type: string example: '39' subscriber_id: type: string example: '15' plan_id: type: string example: '15' status: type: string example: active current_period_start: type: string example: '2026-06-01T00:00:00+00:00' current_period_end: type: string example: '2026-07-01T00:00:00+00:00' trial_ends_at: type: string example: null nullable: true cancel_at: type: string example: null nullable: true canceled_at: type: string example: null nullable: true metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscriptions:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscriptions:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscriptions:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' 422: description: '' content: application/json: schema: oneOf: - description: 'Domain error: `subscription_already_canceled`.' type: object example: error: type: subscription_already_canceled message: 'Subscription is already canceled.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_already_canceled' properties: error: type: object properties: type: type: string example: subscription_already_canceled message: type: string example: 'Subscription is already canceled.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_already_canceled' - description: 'Domain error: `subscription_not_actionable_cancel`.' type: object example: error: type: subscription_not_actionable_cancel message: 'Subscription state does not allow cancellation.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_not_actionable_cancel' properties: error: type: object properties: type: type: string example: subscription_not_actionable_cancel message: type: string example: 'Subscription state does not allow cancellation.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_not_actionable_cancel' - description: 'Domain error: `subscription_missing_stripe_id`.' type: object example: error: type: subscription_missing_stripe_id message: 'Subscription is missing its Stripe identifier.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_missing_stripe_id' properties: error: type: object properties: type: type: string example: subscription_missing_stripe_id message: type: string example: 'Subscription is missing its Stripe identifier.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_missing_stripe_id' tags: - Subscriptions requestBody: required: false content: application/json: schema: type: object properties: at_period_end: type: boolean description: 'When true (default) the subscription is cancelled at the end of the current billing period; when false it is cancelled immediately.' example: true reason: type: string description: 'Optional human-readable reason stored on the subscription metadata.' example: 'Customer requested downgrade' parameters: - in: path name: id description: 'Subscription public id (sub_*) or numeric id.' example: sub_01JXY... required: true schema: type: string '/api/v1/subscriptions/{id}/swap': post: summary: "Swap a subscription's plan price" operationId: swapASubscriptionsPlanPrice description: "Switches the subscription to a different `plan_price_id`. Stripe handles\nproration automatically — pass `proration=\"none\"` to skip it and bill\nthe new price starting from the next cycle." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: subscription id: sub_vmiqlb0ahciyttxovcw2 project_id: '42' subscriber_id: '16' plan_id: '16' status: active current_period_start: '2026-06-01T00:00:00+00:00' current_period_end: '2026-07-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: null created_at: '2026-06-19T09:49:24+00:00' updated_at: '2026-06-19T09:49:24+00:00' properties: object: type: string example: subscription id: type: string example: sub_vmiqlb0ahciyttxovcw2 project_id: type: string example: '42' subscriber_id: type: string example: '16' plan_id: type: string example: '16' status: type: string example: active current_period_start: type: string example: '2026-06-01T00:00:00+00:00' current_period_end: type: string example: '2026-07-01T00:00:00+00:00' trial_ends_at: type: string example: null nullable: true cancel_at: type: string example: null nullable: true canceled_at: type: string example: null nullable: true metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:24+00:00' updated_at: type: string example: '2026-06-19T09:49:24+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscriptions:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscriptions:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscriptions:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' 422: description: '' content: application/json: schema: oneOf: - description: 'Domain error: `subscription_not_actionable_swap`.' type: object example: error: type: subscription_not_actionable_swap message: 'Subscription state does not allow swapping.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_not_actionable_swap' properties: error: type: object properties: type: type: string example: subscription_not_actionable_swap message: type: string example: 'Subscription state does not allow swapping.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_not_actionable_swap' - description: 'Domain error: `subscription_missing_stripe_id`.' type: object example: error: type: subscription_missing_stripe_id message: 'Subscription is missing its Stripe identifier.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_missing_stripe_id' properties: error: type: object properties: type: type: string example: subscription_missing_stripe_id message: type: string example: 'Subscription is missing its Stripe identifier.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_missing_stripe_id' - description: 'Domain error: `plan_archived`.' type: object example: error: type: plan_archived message: 'Plan is archived and cannot be used for new subscriptions.' doc_url: 'https://proxyon.teknoza.be/docs#plan_archived' properties: error: type: object properties: type: type: string example: plan_archived message: type: string example: 'Plan is archived and cannot be used for new subscriptions.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#plan_archived' - description: 'Domain error: `plan_price_inactive`.' type: object example: error: type: plan_price_inactive message: 'Plan price is inactive.' doc_url: 'https://proxyon.teknoza.be/docs#plan_price_inactive' properties: error: type: object properties: type: type: string example: plan_price_inactive message: type: string example: 'Plan price is inactive.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#plan_price_inactive' - description: 'Domain error: `subscription_price_not_synced`.' type: object example: error: type: subscription_price_not_synced message: 'Subscription price is not synced to Stripe.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_price_not_synced' properties: error: type: object properties: type: type: string example: subscription_price_not_synced message: type: string example: 'Subscription price is not synced to Stripe.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_price_not_synced' tags: - Subscriptions requestBody: required: true content: application/json: schema: type: object properties: plan_price_id: type: integer description: 'Identifier of the plan price to swap into.' example: 42 proration: type: string description: 'Proration behaviour. Allowed values: "always" (prorate immediately), "none" (no proration), "always_invoice" (prorate and immediately invoice any proration). Required.' example: always required: - plan_price_id - proration parameters: - in: path name: id description: 'Subscription public id (sub_*) or numeric id.' example: sub_01JXY... required: true schema: type: string '/api/v1/subscriptions/{id}/resume': post: summary: 'Resume a cancelled subscription' operationId: resumeACancelledSubscription description: "Reverts a subscription that was previously cancelled at period end. Only\nworks while the current billing period has not yet ended; after that the\nsubscription must be re-created." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: subscription id: sub_cogmkt8wqnvmmvak7hyi project_id: '45' subscriber_id: '17' plan_id: '17' status: active current_period_start: '2026-06-01T00:00:00+00:00' current_period_end: '2026-07-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: null created_at: '2026-06-19T09:49:24+00:00' updated_at: '2026-06-19T09:49:24+00:00' properties: object: type: string example: subscription id: type: string example: sub_cogmkt8wqnvmmvak7hyi project_id: type: string example: '45' subscriber_id: type: string example: '17' plan_id: type: string example: '17' status: type: string example: active current_period_start: type: string example: '2026-06-01T00:00:00+00:00' current_period_end: type: string example: '2026-07-01T00:00:00+00:00' trial_ends_at: type: string example: null nullable: true cancel_at: type: string example: null nullable: true canceled_at: type: string example: null nullable: true metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:24+00:00' updated_at: type: string example: '2026-06-19T09:49:24+00:00' 403: description: 'Forbidden — API key lacks the required scope `subscriptions:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: subscriptions:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: subscriptions:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' 422: description: '' content: application/json: schema: oneOf: - description: 'Domain error: `subscription_already_canceled`.' type: object example: error: type: subscription_already_canceled message: 'Subscription is already canceled.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_already_canceled' properties: error: type: object properties: type: type: string example: subscription_already_canceled message: type: string example: 'Subscription is already canceled.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_already_canceled' - description: 'Domain error: `subscription_cannot_resume`.' type: object example: error: type: subscription_cannot_resume message: 'Subscription cannot be resumed in its current state.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_cannot_resume' properties: error: type: object properties: type: type: string example: subscription_cannot_resume message: type: string example: 'Subscription cannot be resumed in its current state.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_cannot_resume' - description: 'Domain error: `subscription_missing_stripe_id`.' type: object example: error: type: subscription_missing_stripe_id message: 'Subscription is missing its Stripe identifier.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_missing_stripe_id' properties: error: type: object properties: type: type: string example: subscription_missing_stripe_id message: type: string example: 'Subscription is missing its Stripe identifier.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_missing_stripe_id' tags: - Subscriptions parameters: - in: path name: id description: 'Subscription public id (sub_*) or numeric id.' example: sub_01JXY... required: true schema: type: string /api/v1/plans: get: summary: 'List plans' operationId: listPlans description: "Returns the active plans in your project, each with its active prices\nand the currency they are billed in. Use this to render a pricing page in\nyour own UI; subscribers pick and pay for a plan inside the ProxyOn\nbilling portal (`POST /api/v1/portal-sessions`)." parameters: - in: query name: currency description: 'Filter plans by currency code (e.g. USD, EUR).' example: USD required: false schema: type: string description: 'Filter plans by currency code (e.g. USD, EUR).' example: USD responses: 200: description: 'List of active plans. `data[]` items follow the Plan resource shape (with `prices[]`).' content: application/json: schema: type: object example: object: list data: - object: plan id: '1' key: pro_monthly locale: en name: 'Pro Monthly' description: 'Professional plan billed monthly.' status: active interval_unit: month interval_count: 1 trial_days: 14 metadata: [] created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' prices: [] properties: object: type: string example: list data: type: array example: - object: plan id: '1' key: pro_monthly locale: en name: 'Pro Monthly' description: 'Professional plan billed monthly.' status: active interval_unit: month interval_count: 1 trial_days: 14 metadata: [] created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' prices: [] items: type: object properties: object: type: string example: plan id: type: string example: '1' key: type: string example: pro_monthly locale: type: string example: en name: type: string example: 'Pro Monthly' description: type: string example: 'Professional plan billed monthly.' status: type: string example: active interval_unit: type: string example: month interval_count: type: integer example: 1 trial_days: type: integer example: 14 metadata: type: array example: [] created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-01T00:00:00+00:00' prices: type: array example: [] 403: description: 'Forbidden — API key lacks the required scope `plans:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: plans:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: plans:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' tags: - Plans '/api/v1/plans/{key}': get: summary: 'Retrieve a plan by key' operationId: retrieveAPlanByKey description: "Returns a single plan and its active prices, identified by the\nhuman-readable plan key (e.g. `pro`, `basic-monthly`). Prefer this over\nnumeric ids when wiring up your pricing UI." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: object: plan id: '13' key: aut_adipisci locale: en name: 'Quidem nostrum Plan' description: '' status: draft metadata: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: plan id: type: string example: '13' key: type: string example: aut_adipisci locale: type: string example: en name: type: string example: 'Quidem nostrum Plan' description: type: string example: '' status: type: string example: draft metadata: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `plans:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: plans:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: plans:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Plans parameters: - in: path name: key description: 'The key of the plan.' example: basic-monthly required: true schema: type: string /api/v1/portal-sessions: post: summary: 'Create a billing portal session' operationId: createABillingPortalSession description: "Issues a single-use magic link to the project's whitelabel ProxyOn\nbilling portal so the subscriber can choose or change a plan, update their\npayment method, view invoices and cancel on their own. Redirect them to\nthe returned `url`; when they leave the portal they are sent back to\n`return_url`." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: 'Portal session created. Redirect the subscriber to the returned URL.' content: application/json: schema: type: object example: url: 'https://billing.example.com/portal/abcdef0123456789' expires_at: '2026-01-15T10:15:00+00:00' properties: url: type: string example: 'https://billing.example.com/portal/abcdef0123456789' expires_at: type: string example: '2026-01-15T10:15:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `portal:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: portal:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: portal:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Domain error: `subscriber_not_found`.' content: application/json: schema: type: object example: error: type: subscriber_not_found message: 'Subscriber not found for this project.' doc_url: 'https://proxyon.teknoza.be/docs#subscriber_not_found' properties: error: type: object properties: type: type: string example: subscriber_not_found message: type: string example: 'Subscriber not found for this project.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscriber_not_found' 409: description: 'Domain error: `stripe_not_connected`.' content: application/json: schema: type: object example: error: type: stripe_not_connected message: 'Connect and verify your Stripe account before using checkout or billing portal.' doc_url: 'https://proxyon.teknoza.be/docs#stripe_not_connected' properties: error: type: object properties: type: type: string example: stripe_not_connected message: type: string example: 'Connect and verify your Stripe account before using checkout or billing portal.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#stripe_not_connected' 422: description: 'Domain error: `subscriber_wrong_project`.' content: application/json: schema: type: object example: error: type: subscriber_wrong_project message: 'Subscriber does not belong to the authenticated project.' doc_url: 'https://proxyon.teknoza.be/docs#subscriber_wrong_project' properties: error: type: object properties: type: type: string example: subscriber_wrong_project message: type: string example: 'Subscriber does not belong to the authenticated project.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscriber_wrong_project' tags: - Portal requestBody: required: true content: application/json: schema: type: object properties: subscriber_external_id: type: string description: 'External identifier of the subscriber to authenticate into the portal.' example: user_12345 return_url: type: string description: 'URL the portal redirects the subscriber to when they leave.' example: 'https://app.example.com/account' required: - subscriber_external_id - return_url '/api/v1/subscribers/{external_id}/entitlements': get: summary: "Resolve a subscriber's entitlements" operationId: resolveASubscribersEntitlements description: "Returns the resolved feature flags and limits the subscriber currently\nhas access to, based on their active subscription's plan. Cache this on\nthe client for 60 seconds — Proxyon returns a strong `ETag` so you can\nrevalidate with `If-None-Match` for a cheap `304 Not Modified`." parameters: - in: header name: If-None-Match description: '' example: '"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"' schema: type: string responses: 200: description: 'Returned with `Cache-Control: private, max-age=60` and a strong `ETag` of the JSON body. Send that ETag back as `If-None-Match` for a cheap revalidation.' content: application/json: schema: type: object example: object: entitlements data: subscriber_id: 42 project_id: 7 subscription_id: 311 plan: key: pro name: Pro description: 'Professional plan.' interval_unit: month interval_count: 1 seats: 5 entries: - key: projects type: quota value: 5 remaining: 3 period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' - key: sso type: boolean value: true remaining: null period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' generated_at: '2026-06-10T12:00:00+00:00' properties: object: type: string example: entitlements data: type: object properties: subscriber_id: type: integer example: 42 project_id: type: integer example: 7 subscription_id: type: integer example: 311 plan: type: object properties: key: type: string example: pro name: type: string example: Pro description: type: string example: 'Professional plan.' interval_unit: type: string example: month interval_count: type: integer example: 1 seats: type: integer example: 5 entries: type: array example: - key: projects type: quota value: 5 remaining: 3 period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' - key: sso type: boolean value: true remaining: null period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' items: type: object properties: key: type: string example: projects type: type: string example: quota value: type: integer example: 5 remaining: type: integer example: 3 period_start: type: string example: '2026-06-01T00:00:00+00:00' period_end: type: string example: '2026-07-01T00:00:00+00:00' generated_at: type: string example: '2026-06-10T12:00:00+00:00' 304: description: 'Entitlement set unchanged since the ETag in `If-None-Match`.' content: application/json: schema: type: object nullable: true 403: description: 'Forbidden — API key lacks the required scope `entitlements:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: entitlements:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: entitlements:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Entitlements parameters: - in: path name: external_id description: 'External identifier of the subscriber.' example: user_12345 required: true schema: type: string '/api/v1/subscribers/{external_id}/usage': get: summary: "Get a subscriber's usage summary" operationId: getASubscribersUsageSummary description: "Aggregates the subscriber's recorded usage for a single metered feature\nwithin the current billing period. Returns `404 no_active_subscription`\nwhen the subscriber has no active subscription to report against." parameters: - in: query name: feature_key description: 'Feature key to summarise usage for.' example: api_calls required: true schema: type: string description: 'Feature key to summarise usage for.' example: api_calls - in: query name: period description: 'Period to aggregate: "current" (default — the active billing period), "last" (the billing period immediately before the current one), or "last_30d" (rolling window over the last 30 days).' example: current required: false schema: type: string description: 'Period to aggregate: "current" (default — the active billing period), "last" (the billing period immediately before the current one), or "last_30d" (rolling window over the last 30 days).' example: current responses: 200: description: "Aggregated usage for the subscriber's active subscription, scoped to the current billing period." content: application/json: schema: type: object example: object: usage_summary feature_key: api_calls quantity: 1284 period_start: '2026-05-01T00:00:00+00:00' period_end: '2026-06-01T00:00:00+00:00' subscription_public_id: sub_01JXY... properties: object: type: string example: usage_summary feature_key: type: string example: api_calls quantity: type: integer example: 1284 period_start: type: string example: '2026-05-01T00:00:00+00:00' period_end: type: string example: '2026-06-01T00:00:00+00:00' subscription_public_id: type: string example: sub_01JXY... 403: description: 'Forbidden — API key lacks the required scope `usage:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: usage:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: usage:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: '' content: application/json: schema: oneOf: - description: 'Subscriber has no active subscription to summarise usage against.' type: object example: error: type: no_active_subscription message: 'Subscriber has no active subscription.' doc_url: '/docs#no_active_subscription' properties: error: type: object properties: type: type: string example: no_active_subscription message: type: string example: 'Subscriber has no active subscription.' doc_url: type: string example: '/docs#no_active_subscription' - description: 'Not Found — the referenced resource does not exist for this project.' type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Usage requestBody: required: true content: application/json: schema: type: object properties: feature_key: type: string description: 'Must match the regex /^[a-z][a-z0-9_]{0,254}$/. Must not be greater than 255 characters.' example: b period: type: string description: '' example: current enum: - current - last - last_30d required: - feature_key parameters: - in: path name: external_id description: 'External identifier of the subscriber.' example: user_12345 required: true schema: type: string /api/v1/usage: post: summary: 'Record metered usage' operationId: recordMeteredUsage description: "Records a usage event against the subscriber's active metered\nsubscription item. Use this whenever the subscriber consumes a billable\nfeature (API call, transcoded video, sent email, …).\n\nPass a stable `idempotency_key` per logical event — the same key always\nreturns the same record, so it is safe to retry on network errors. The\nrecorded quantity is forwarded to Stripe and reflected in the next\ninvoice for the subscriber's current billing period." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: usage_record id: '2' feature_id: '7' quantity: 7 recorded_at: '2026-06-19T09:49:24+00:00' idempotency_key: usage_c1chv8pgu595azfpwdxsd6qa created_at: '2026-06-19T09:49:24+00:00' updated_at: '2026-06-19T09:49:24+00:00' properties: object: type: string example: usage_record id: type: string example: '2' feature_id: type: string example: '7' quantity: type: integer example: 7 recorded_at: type: string example: '2026-06-19T09:49:24+00:00' idempotency_key: type: string example: usage_c1chv8pgu595azfpwdxsd6qa created_at: type: string example: '2026-06-19T09:49:24+00:00' updated_at: type: string example: '2026-06-19T09:49:24+00:00' 403: description: 'Forbidden — API key lacks the required scope `usage:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: usage:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: usage:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Domain error: `no_active_subscription`.' content: application/json: schema: type: object example: error: type: no_active_subscription message: 'This subscriber has no active subscription.' doc_url: 'https://proxyon.teknoza.be/docs#no_active_subscription' properties: error: type: object properties: type: type: string example: no_active_subscription message: type: string example: 'This subscriber has no active subscription.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#no_active_subscription' 422: description: '' content: application/json: schema: oneOf: - description: 'Domain error: `usage_invalid_quantity`.' type: object example: error: type: usage_invalid_quantity message: 'Usage quantity must be a positive integer.' doc_url: 'https://proxyon.teknoza.be/docs#usage_invalid_quantity' properties: error: type: object properties: type: type: string example: usage_invalid_quantity message: type: string example: 'Usage quantity must be a positive integer.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_invalid_quantity' - description: 'Domain error: `usage_correction_requires_sum_aggregation`.' type: object example: error: type: usage_correction_requires_sum_aggregation message: 'Usage corrections require sum aggregation.' doc_url: 'https://proxyon.teknoza.be/docs#usage_correction_requires_sum_aggregation' properties: error: type: object properties: type: type: string example: usage_correction_requires_sum_aggregation message: type: string example: 'Usage corrections require sum aggregation.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_correction_requires_sum_aggregation' - description: 'Domain error: `usage_recorded_at_in_future`.' type: object example: error: type: usage_recorded_at_in_future message: 'recorded_at must be at or before the current server time.' doc_url: 'https://proxyon.teknoza.be/docs#usage_recorded_at_in_future' properties: error: type: object properties: type: type: string example: usage_recorded_at_in_future message: type: string example: 'recorded_at must be at or before the current server time.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_recorded_at_in_future' - description: 'Domain error: `usage_recorded_at_too_old`.' type: object example: error: type: usage_recorded_at_too_old message: 'recorded_at is older than the accepted backdating window.' doc_url: 'https://proxyon.teknoza.be/docs#usage_recorded_at_too_old' properties: error: type: object properties: type: type: string example: usage_recorded_at_too_old message: type: string example: 'recorded_at is older than the accepted backdating window.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_recorded_at_too_old' - description: 'Domain error: `usage_subscription_not_active`.' type: object example: error: type: usage_subscription_not_active message: 'Subscription is not active; usage cannot be recorded.' doc_url: 'https://proxyon.teknoza.be/docs#usage_subscription_not_active' properties: error: type: object properties: type: type: string example: usage_subscription_not_active message: type: string example: 'Subscription is not active; usage cannot be recorded.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_subscription_not_active' - description: 'Domain error: `usage_unsupported_feature_type`.' type: object example: error: type: usage_unsupported_feature_type message: 'Feature type does not support usage records.' doc_url: 'https://proxyon.teknoza.be/docs#usage_unsupported_feature_type' properties: error: type: object properties: type: type: string example: usage_unsupported_feature_type message: type: string example: 'Feature type does not support usage records.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_unsupported_feature_type' - description: 'Domain error: `usage_feature_not_in_plan`.' type: object example: error: type: usage_feature_not_in_plan message: 'Feature is not part of the subscription plan.' doc_url: 'https://proxyon.teknoza.be/docs#usage_feature_not_in_plan' properties: error: type: object properties: type: type: string example: usage_feature_not_in_plan message: type: string example: 'Feature is not part of the subscription plan.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_feature_not_in_plan' - description: 'Domain error: `subscription_no_items`.' type: object example: error: type: subscription_no_items message: 'Subscription has no priced items.' doc_url: 'https://proxyon.teknoza.be/docs#subscription_no_items' properties: error: type: object properties: type: type: string example: subscription_no_items message: type: string example: 'Subscription has no priced items.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#subscription_no_items' - description: 'Domain error: `plan_cross_project_resource`.' type: object example: error: type: plan_cross_project_resource message: 'Plan resource belongs to a different project.' doc_url: 'https://proxyon.teknoza.be/docs#plan_cross_project_resource' properties: error: type: object properties: type: type: string example: plan_cross_project_resource message: type: string example: 'Plan resource belongs to a different project.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#plan_cross_project_resource' tags: - Usage requestBody: required: true content: application/json: schema: type: object properties: subscriber_external_id: type: string description: 'External identifier of the subscriber whose subscription will be charged.' example: user_12345 feature_key: type: string description: 'Metered feature key to record usage against.' example: api_calls quantity: type: integer description: 'Quantity to record. Non-zero integer. Positive values record consumption; negative values post a correction (only allowed on Metered features whose Stripe Meter uses `sum` aggregation, or on Quota features).' example: 10 idempotency_key: type: string description: 'Unique key to deduplicate this usage record (max 100 chars — forwarded to Stripe as the meter event identifier). Same key returns the same record.' example: 01JXY7QGJ8KZ... recorded_at: type: string description: 'ISO-8601 timestamp when usage was observed. Defaults to the server time.' example: '2026-01-15T10:00:00Z' required: - subscriber_external_id - feature_key - quantity - idempotency_key '/api/v1/subscribers/{external_id}/invoices': get: summary: "List a subscriber's invoices" operationId: listASubscribersInvoices description: "Returns invoices for the subscriber in reverse chronological order,\nincluding hosted invoice URLs and PDF links generated by Stripe. Ideal\nfor a \"Billing history\" tab in your application." parameters: [] responses: 200: description: 'List of invoices for the subscriber, newest first. `data[]` items follow the Invoice resource shape.' content: application/json: schema: type: object example: object: list data: - object: invoice id: inv_01HXYZ subscription_id: '1' currency_id: '1' currency_code: USD status: paid amount_due: 2900 amount_paid: 2900 hosted_invoice_url: 'https://invoice.stripe.com/i/acct_xxx/inv_xxx' pdf_url: 'https://pay.stripe.com/invoice/xxx/pdf' period_start: '2026-05-01T00:00:00+00:00' period_end: '2026-06-01T00:00:00+00:00' paid_at: '2026-05-02T10:00:00+00:00' created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-02T10:00:00+00:00' properties: object: type: string example: list data: type: array example: - object: invoice id: inv_01HXYZ subscription_id: '1' currency_id: '1' currency_code: USD status: paid amount_due: 2900 amount_paid: 2900 hosted_invoice_url: 'https://invoice.stripe.com/i/acct_xxx/inv_xxx' pdf_url: 'https://pay.stripe.com/invoice/xxx/pdf' period_start: '2026-05-01T00:00:00+00:00' period_end: '2026-06-01T00:00:00+00:00' paid_at: '2026-05-02T10:00:00+00:00' created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-02T10:00:00+00:00' items: type: object properties: object: type: string example: invoice id: type: string example: inv_01HXYZ subscription_id: type: string example: '1' currency_id: type: string example: '1' currency_code: type: string example: USD status: type: string example: paid amount_due: type: integer example: 2900 amount_paid: type: integer example: 2900 hosted_invoice_url: type: string example: 'https://invoice.stripe.com/i/acct_xxx/inv_xxx' pdf_url: type: string example: 'https://pay.stripe.com/invoice/xxx/pdf' period_start: type: string example: '2026-05-01T00:00:00+00:00' period_end: type: string example: '2026-06-01T00:00:00+00:00' paid_at: type: string example: '2026-05-02T10:00:00+00:00' created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-02T10:00:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `invoices:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: invoices:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: invoices:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Invoices parameters: - in: path name: external_id description: 'External identifier of the subscriber.' example: user_12345 required: true schema: type: string '/api/v1/invoices/{id}': get: summary: 'Retrieve an invoice' operationId: retrieveAnInvoice description: "Returns a single invoice by its public id (`in_*`) or numeric id,\nincluding the hosted Stripe URL and PDF download link." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: object: invoice id: in_kzgiimwbgpfca6t8tif0 subscription_id: '8' currency_id: '1' status: open amount_due: 49055 amount_paid: 0 hosted_invoice_url: null pdf_url: null period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' paid_at: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: invoice id: type: string example: in_kzgiimwbgpfca6t8tif0 subscription_id: type: string example: '8' currency_id: type: string example: '1' status: type: string example: open amount_due: type: integer example: 49055 amount_paid: type: integer example: 0 hosted_invoice_url: type: string example: null nullable: true pdf_url: type: string example: null nullable: true period_start: type: string example: '2026-06-01T00:00:00+00:00' period_end: type: string example: '2026-07-01T00:00:00+00:00' paid_at: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `invoices:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: invoices:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: invoices:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - Invoices parameters: - in: path name: id description: 'Invoice public id (in_*) or numeric id.' example: in_01JXY... required: true schema: type: string /api/v1/webhook-endpoints: get: summary: 'List webhook endpoints' operationId: listWebhookEndpoints description: "Returns every webhook endpoint configured for your project, with their\ncurrent status and which event types they subscribe to. Use this to\nrender a webhooks settings page in your dashboard." parameters: [] responses: 200: description: 'List of webhook endpoints. `data[]` items follow the WebhookEndpoint resource shape.' content: application/json: schema: type: object example: object: list data: - object: webhook_endpoint id: 1 url: 'https://example.test/webhooks/proxyon' description: 'Production webhook' status: active event_types: - subscription.created - invoice.paid consecutive_failures: 0 last_success_at: '2026-05-20T09:00:00+00:00' last_failure_at: null signing_secret_grace_ends_at: null created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-20T09:00:00+00:00' properties: object: type: string example: list data: type: array example: - object: webhook_endpoint id: 1 url: 'https://example.test/webhooks/proxyon' description: 'Production webhook' status: active event_types: - subscription.created - invoice.paid consecutive_failures: 0 last_success_at: '2026-05-20T09:00:00+00:00' last_failure_at: null signing_secret_grace_ends_at: null created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-20T09:00:00+00:00' items: type: object properties: object: type: string example: webhook_endpoint id: type: integer example: 1 url: type: string example: 'https://example.test/webhooks/proxyon' description: type: string example: 'Production webhook' status: type: string example: active event_types: type: array example: - subscription.created - invoice.paid items: type: string consecutive_failures: type: integer example: 0 last_success_at: type: string example: '2026-05-20T09:00:00+00:00' last_failure_at: type: string example: null nullable: true signing_secret_grace_ends_at: type: string example: null nullable: true created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-20T09:00:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `webhooks:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: webhooks:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: webhooks:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' tags: - 'Webhook Endpoints' post: summary: 'Create a webhook endpoint' operationId: createAWebhookEndpoint description: "Registers a new HTTPS URL to receive Proxyon webhook deliveries. The\nresponse contains a one-time-visible signing `secret` — store it\nsecurely on your server and use it to verify the `Proxyon-Signature`\nheader on every delivery." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 201: description: 'Webhook endpoint created. `signing_secret` is exposed **once** — store it securely; subsequent reads of this endpoint will omit it.' content: application/json: schema: type: object example: object: webhook_endpoint id: 1 url: 'https://app.example.com/webhooks/proxyon' description: 'Production receiver' status: active event_types: - subscription.created - invoice.paid consecutive_failures: 0 last_success_at: null last_failure_at: null signing_secret: whsec_01JXY7Z3K2M5N6P8Q9R0S1T2U3 signing_secret_grace_ends_at: null created_at: '2026-05-20T10:00:00+00:00' updated_at: '2026-05-20T10:00:00+00:00' properties: object: type: string example: webhook_endpoint id: type: integer example: 1 url: type: string example: 'https://app.example.com/webhooks/proxyon' description: type: string example: 'Production receiver' status: type: string example: active event_types: type: array example: - subscription.created - invoice.paid items: type: string consecutive_failures: type: integer example: 0 last_success_at: type: string example: null nullable: true last_failure_at: type: string example: null nullable: true signing_secret: type: string example: whsec_01JXY7Z3K2M5N6P8Q9R0S1T2U3 signing_secret_grace_ends_at: type: string example: null nullable: true created_at: type: string example: '2026-05-20T10:00:00+00:00' updated_at: type: string example: '2026-05-20T10:00:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `webhooks:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: webhooks:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: webhooks:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' tags: - 'Webhook Endpoints' requestBody: required: true content: application/json: schema: type: object properties: url: type: string description: 'HTTPS URL to receive webhook deliveries.' example: 'https://app.example.com/webhooks/proxyon' event_types: type: array description: 'List of webhook event types to subscribe to.' example: - subscription.created - invoice.paid items: type: string description: type: string description: 'Optional description for this endpoint.' example: 'Production receiver' required: - url - event_types '/api/v1/webhook-endpoints/{id}': get: summary: 'Retrieve a webhook endpoint' operationId: retrieveAWebhookEndpoint description: "Returns a single webhook endpoint, including its current delivery\nstatus and last-success / failure counters." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: object: webhook_endpoint id: 3 url: 'https://example.com/webhooks/bfc53181-d647-36b2-9080-f9c2b76006f4' description: 'Qui commodi incidunt iure odit.' status: active event_types: [] consecutive_failures: 0 last_success_at: null last_failure_at: null signing_secret_grace_ends_at: null created_at: '2026-06-19T09:49:23+00:00' updated_at: '2026-06-19T09:49:23+00:00' properties: object: type: string example: webhook_endpoint id: type: integer example: 3 url: type: string example: 'https://example.com/webhooks/bfc53181-d647-36b2-9080-f9c2b76006f4' description: type: string example: 'Qui commodi incidunt iure odit.' status: type: string example: active event_types: type: array example: [] consecutive_failures: type: integer example: 0 last_success_at: type: string example: null nullable: true last_failure_at: type: string example: null nullable: true signing_secret_grace_ends_at: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:23+00:00' updated_at: type: string example: '2026-06-19T09:49:23+00:00' 403: description: 'Forbidden — API key lacks the required scope `webhooks:read`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: webhooks:read' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: webhooks:read' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - 'Webhook Endpoints' patch: summary: 'Update a webhook endpoint' operationId: updateAWebhookEndpoint description: "Updates the URL, description, subscribed events or active status of a\nwebhook endpoint. Setting `status=\"disabled\"` pauses deliveries without\nlosing the signing secret." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: object: webhook_endpoint id: 4 url: 'https://example.com/webhooks/a4855dc5-0acb-33c3-b921-f4291f719ca0' description: null status: active event_types: [] consecutive_failures: 0 last_success_at: null last_failure_at: null signing_secret_grace_ends_at: null created_at: '2026-06-19T09:49:24+00:00' updated_at: '2026-06-19T09:49:24+00:00' properties: object: type: string example: webhook_endpoint id: type: integer example: 4 url: type: string example: 'https://example.com/webhooks/a4855dc5-0acb-33c3-b921-f4291f719ca0' description: type: string example: null nullable: true status: type: string example: active event_types: type: array example: [] consecutive_failures: type: integer example: 0 last_success_at: type: string example: null nullable: true last_failure_at: type: string example: null nullable: true signing_secret_grace_ends_at: type: string example: null nullable: true created_at: type: string example: '2026-06-19T09:49:24+00:00' updated_at: type: string example: '2026-06-19T09:49:24+00:00' 403: description: 'Forbidden — API key lacks the required scope `webhooks:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: webhooks:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: webhooks:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - 'Webhook Endpoints' requestBody: required: false content: application/json: schema: type: object properties: url: type: string description: 'New HTTPS URL.' example: 'http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html' event_types: type: array description: 'Replace the full set of subscribed event types.' example: - architecto items: type: string description: type: string description: 'New description.' example: 'Eius et animi quos velit et.' status: type: string description: 'New status: "active" or "disabled".' example: architecto delete: summary: 'Delete a webhook endpoint' operationId: deleteAWebhookEndpoint description: "Permanently removes a webhook endpoint. In-flight deliveries are\naborted; pending retries are discarded." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 204: description: 'Webhook endpoint deleted.' content: application/json: schema: type: object nullable: true 403: description: 'Forbidden — API key lacks the required scope `webhooks:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: webhooks:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: webhooks:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - 'Webhook Endpoints' parameters: - in: path name: id description: 'The webhook endpoint id.' example: 16 required: true schema: type: integer '/api/v1/webhook-endpoints/{id}/rotate': post: summary: 'Rotate the signing secret' operationId: rotateTheSigningSecret description: "Generates a new signing `secret` for the webhook endpoint and returns it\nin the response (one-time view). The previous secret continues to sign\ndeliveries for a 24-hour grace period so you can roll out the new one\nwithout dropping events." parameters: - in: header name: Idempotency-Key description: '' example: idem_01JXY... schema: type: string responses: 200: description: 'Rotated. The new `signing_secret` is returned **once** in plaintext. The previous secret remains valid for a grace window (see `signing_secret_grace_ends_at`).' content: application/json: schema: type: object example: object: webhook_endpoint id: 1 url: 'https://app.example.com/webhooks/proxyon' description: 'Production receiver' status: active event_types: - subscription.created - invoice.paid consecutive_failures: 0 last_success_at: '2026-05-20T09:00:00+00:00' last_failure_at: null signing_secret: whsec_NEW01JXY7Z3K2M5N6P8Q9R0S1T2 signing_secret_grace_ends_at: '2026-05-21T10:00:00+00:00' created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-20T10:00:00+00:00' properties: object: type: string example: webhook_endpoint id: type: integer example: 1 url: type: string example: 'https://app.example.com/webhooks/proxyon' description: type: string example: 'Production receiver' status: type: string example: active event_types: type: array example: - subscription.created - invoice.paid items: type: string consecutive_failures: type: integer example: 0 last_success_at: type: string example: '2026-05-20T09:00:00+00:00' last_failure_at: type: string example: null nullable: true signing_secret: type: string example: whsec_NEW01JXY7Z3K2M5N6P8Q9R0S1T2 signing_secret_grace_ends_at: type: string example: '2026-05-21T10:00:00+00:00' created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-20T10:00:00+00:00' 403: description: 'Forbidden — API key lacks the required scope `webhooks:write`.' content: application/json: schema: type: object example: error: type: insufficient_scope message: 'API key lacks required scope: webhooks:write' doc_url: 'https://proxyon.teknoza.be/docs#insufficient_scope' properties: error: type: object properties: type: type: string example: insufficient_scope message: type: string example: 'API key lacks required scope: webhooks:write' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#insufficient_scope' 404: description: 'Not Found — the referenced resource does not exist for this project.' content: application/json: schema: type: object example: error: type: not_found message: 'The requested resource was not found.' doc_url: 'https://proxyon.teknoza.be/docs#not_found' properties: error: type: object properties: type: type: string example: not_found message: type: string example: 'The requested resource was not found.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#not_found' tags: - 'Webhook Endpoints' parameters: - in: path name: id description: 'The webhook endpoint id.' example: 16 required: true schema: type: integer