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 and discover subscribable events' - name: Health description: 'Liveness probe for the public API' 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, newest first, with\nStripe-style cursor pagination: pass the last item's `id` as\n`starting_after` to fetch the next page, until `has_more` is `false`.\nUse the `email` filter to look one up by address, or `type` to narrow\nthe list to users or organizations." 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 - in: query name: limit description: 'Page size, 1-100. Defaults to 100.' example: 25 required: false schema: type: integer description: 'Page size, 1-100. Defaults to 100.' example: 25 - in: query name: starting_after description: 'Cursor: the `id` of the last subscriber on the previous page.' example: sbr_01HXYZ required: false schema: type: string description: 'Cursor: the `id` of the last subscriber on the previous page.' example: sbr_01HXYZ responses: 200: description: 'List wrapper. `data[]` items follow the Subscriber resource shape; `has_more` signals another page behind the `starting_after` cursor.' content: application/json: schema: type: object example: object: list has_more: false 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 has_more: type: boolean example: false 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_1rbq6dehld2s0owrkkpe external_id: usr_4g2uspo1lkmj type: user email: wleuschke@example.net name: 'Mina Bauch' metadata: {} created_at: '2026-08-11T13:51:38+00:00' updated_at: '2026-08-11T13:51:38+00:00' properties: object: type: string example: subscriber id: type: string example: sbr_1rbq6dehld2s0owrkkpe external_id: type: string example: usr_4g2uspo1lkmj type: type: string example: user email: type: string example: wleuschke@example.net name: type: string example: 'Mina Bauch' metadata: type: object properties: {} created_at: type: string example: '2026-08-11T13:51:38+00:00' updated_at: type: string example: '2026-08-11T13:51:38+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: 'Your own identifier for the user or tenant this subscriber represents — whatever primary id you already use in your application. Proxyon stores it as an opaque string with no required format or prefix (allowed characters: letters, digits and `_ . - :`). The `user_12345` example is illustrative only; a bare `42` or a UUID work just as well.' 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' currency: type: string description: "ISO 4217 code of the currency this subscriber is billed in (must be one of the project's enabled currencies). The whitelabel portal prices every plan in this currency for the subscriber — the end user cannot switch it. Omit or send null to inherit the project default currency." example: EUR 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_rx1mhn08f3kxumjnit9w external_id: usr_ujagszu3nqpx type: user email: price.amber@example.org name: 'Miss Jazlyn Keebler III' metadata: {} created_at: '2026-08-11T13:51:37+00:00' updated_at: '2026-08-11T13:51:37+00:00' properties: object: type: string example: subscriber id: type: string example: sbr_rx1mhn08f3kxumjnit9w external_id: type: string example: usr_ujagszu3nqpx type: type: string example: user email: type: string example: price.amber@example.org name: type: string example: 'Miss Jazlyn Keebler III' metadata: type: object properties: {} created_at: type: string example: '2026-08-11T13:51:37+00:00' updated_at: type: string example: '2026-08-11T13:51:37+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_gkt4rjckw0zby5nt83cn external_id: usr_u6v0mp5kgyhv type: user email: ferne52@example.com name: 'Mr. Carey Smitham' metadata: {} created_at: '2026-08-11T13:51:38+00:00' updated_at: '2026-08-11T13:51:38+00:00' properties: object: type: string example: subscriber id: type: string example: sbr_gkt4rjckw0zby5nt83cn external_id: type: string example: usr_u6v0mp5kgyhv type: type: string example: user email: type: string example: ferne52@example.com name: type: string example: 'Mr. Carey Smitham' metadata: type: object properties: {} created_at: type: string example: '2026-08-11T13:51:38+00:00' updated_at: type: string example: '2026-08-11T13:51:38+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' currency: type: string description: "ISO 4217 billing currency code (one of the project's enabled currencies), or null to inherit the project default. Drives the portal pricing currency for this subscriber." example: EUR 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_rf29b606bhup8zr9rujq project_id: '51' subscriber_id: '21' plan_id: '16' status: active current_period_start: '2026-08-01T00:00:00+00:00' current_period_end: '2026-09-01T00:00:00+00:00' trial_ends_at: null cancel_at: null canceled_at: null metadata: {} created_at: '2026-08-11T13:51:37+00:00' updated_at: '2026-08-11T13:51:37+00:00' properties: object: type: string example: subscription id: type: string example: sub_rf29b606bhup8zr9rujq project_id: type: string example: '51' subscriber_id: type: string example: '21' plan_id: type: string example: '16' status: type: string example: active current_period_start: type: string example: '2026-08-01T00:00:00+00:00' current_period_end: type: string example: '2026-09-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: object properties: {} created_at: type: string example: '2026-08-11T13:51:37+00:00' updated_at: type: string example: '2026-08-11T13:51:37+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/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, each with its catalog `features[]` and active `prices[]`. Use `features[]` to render what a plan includes without creating a subscriber, and `is_default` to resolve the project's default/free tier — never infer it from `key`." 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 is_default: false metadata: highlight: 'true' created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' features: - key: projects name: Projects type: quota value: 10 - key: sso name: SSO type: boolean value: true - key: ai_tokens name: 'AI tokens' type: metered value: 100000 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 is_default: false metadata: highlight: 'true' created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' features: - key: projects name: Projects type: quota value: 10 - key: sso name: SSO type: boolean value: true - key: ai_tokens name: 'AI tokens' type: metered value: 100000 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 is_default: type: boolean example: false metadata: type: object properties: highlight: type: string example: 'true' created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-01T00:00:00+00:00' features: type: array example: - key: projects name: Projects type: quota value: 10 - key: sso name: SSO type: boolean value: true - key: ai_tokens name: 'AI tokens' type: metered value: 100000 items: type: object properties: key: type: string example: projects name: type: string example: Projects type: type: string example: quota value: type: integer example: 10 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: 'A single plan with its catalog `features[]` and active `prices[]`.' content: application/json: schema: type: object example: object: plan id: '1' key: pro_monthly locale: en name: 'Pro Monthly' description: 'Professional plan billed monthly.' status: active is_default: false metadata: highlight: 'true' created_at: '2026-05-01T00:00:00+00:00' updated_at: '2026-05-01T00:00:00+00:00' features: - key: projects name: Projects type: quota value: 10 - key: sso name: SSO type: boolean value: true prices: [] 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 is_default: type: boolean example: false metadata: type: object properties: highlight: type: string example: 'true' created_at: type: string example: '2026-05-01T00:00:00+00:00' updated_at: type: string example: '2026-05-01T00:00:00+00:00' features: type: array example: - key: projects name: Projects type: quota value: 10 - key: sso name: SSO type: boolean value: true items: type: object properties: key: type: string example: projects name: type: string example: Projects type: type: string example: quota value: type: integer example: 10 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' 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`.\n\nTreat `url` as opaque: its host is your verified custom domain when one is\nconfigured, otherwise the ProxyOn shared host. Do not parse or rebuild it." 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`.\n\nAn entry states what the plan *grants*, never what is left: Proxyon does\nnot know how many devices or seats you have created. Count usage in your\nown application and compare it against `value` (`-1` means unlimited).\n\n`subscription` is `null` for a subscriber with no live subscription, and\n`plan.is_default` marks the project's free tier — never infer the free\nplan from `plan.key`, which each project chooses for itself." 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.' is_default: false interval_unit: month interval_count: 1 seats: 5 subscription: status: active cancel_at: null current_period_end: '2026-07-01T00:00:00+00:00' trial_ends_at: null entries: - key: projects type: quota value: 5 period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' meter_key: project_events - key: sso type: boolean value: true period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' meter_key: null 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.' is_default: type: boolean example: false interval_unit: type: string example: month interval_count: type: integer example: 1 seats: type: integer example: 5 subscription: type: object properties: status: type: string example: active cancel_at: type: string example: null nullable: true current_period_end: type: string example: '2026-07-01T00:00:00+00:00' trial_ends_at: type: string example: null nullable: true entries: type: array example: - key: projects type: quota value: 5 period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' meter_key: project_events - key: sso type: boolean value: true period_start: '2026-06-01T00:00:00+00:00' period_end: '2026-07-01T00:00:00+00:00' meter_key: null items: type: object properties: key: type: string example: projects type: type: string example: quota value: type: integer example: 5 period_start: type: string example: '2026-06-01T00:00:00+00:00' period_end: type: string example: '2026-07-01T00:00:00+00:00' meter_key: type: string example: project_events 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 meter within the\ncurrent billing period. Returns `404 no_active_subscription` when the\nsubscriber has no active subscription to report against." parameters: - in: query name: meter_key description: 'Meter key to summarise usage for.' example: api_calls required: true schema: type: string description: 'Meter 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 meter_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 meter_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 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: '4' meter_id: '5' quantity: 81 recorded_at: '2026-08-11T13:51:38+00:00' idempotency_key: usage_70ceuelwr4wf5rsdtv4cbfab created_at: '2026-08-11T13:51:38+00:00' updated_at: '2026-08-11T13:51:38+00:00' properties: object: type: string example: usage_record id: type: string example: '4' meter_id: type: string example: '5' quantity: type: integer example: 81 recorded_at: type: string example: '2026-08-11T13:51:38+00:00' idempotency_key: type: string example: usage_70ceuelwr4wf5rsdtv4cbfab created_at: type: string example: '2026-08-11T13:51:38+00:00' updated_at: type: string example: '2026-08-11T13:51:38+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_no_metered_item_for_meter`.' type: object example: error: type: usage_no_metered_item_for_meter message: 'No metered subscription item is bound to this meter.' doc_url: 'https://proxyon.teknoza.be/docs#usage_no_metered_item_for_meter' properties: error: type: object properties: type: type: string example: usage_no_metered_item_for_meter message: type: string example: 'No metered subscription item is bound to this meter.' doc_url: type: string example: 'https://proxyon.teknoza.be/docs#usage_no_metered_item_for_meter' - 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 meter_key: type: string description: 'Meter 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 meters using `sum` aggregation).' 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 - meter_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_biz1pbpsve1sud0mf1fh subscription_id: '13' currency_id: '1' status: open amount_due: 49055 amount_paid: 0 hosted_invoice_url: null pdf_url: null period_start: '2026-08-01T00:00:00+00:00' period_end: '2026-09-01T00:00:00+00:00' paid_at: null created_at: '2026-08-11T13:51:37+00:00' updated_at: '2026-08-11T13:51:37+00:00' properties: object: type: string example: invoice id: type: string example: in_biz1pbpsve1sud0mf1fh subscription_id: type: string example: '13' 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-08-01T00:00:00+00:00' period_end: type: string example: '2026-09-01T00:00:00+00:00' paid_at: type: string example: null nullable: true created_at: type: string example: '2026-08-11T13:51:37+00:00' updated_at: type: string example: '2026-08-11T13:51:37+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-events: get: summary: 'List subscribable webhook event types' operationId: listSubscribableWebhookEventTypes description: "Machine-readable catalog of every event type an endpoint can subscribe\nto. Use it to render a subscription picker or to validate your stored\nevent list against the current platform catalog instead of hardcoding\nevent names." parameters: [] responses: 200: description: 'Every subscribable event type with its human-readable label.' content: application/json: schema: type: object example: data: - type: subscriber.created label: 'Subscriber created' - type: subscription.created label: 'Subscription created' - type: entitlement.updated label: 'Entitlements updated' properties: data: type: array example: - type: subscriber.created label: 'Subscriber created' - type: subscription.created label: 'Subscription created' - type: entitlement.updated label: 'Entitlements updated' items: type: object properties: type: type: string example: subscriber.created label: type: string example: 'Subscriber created' 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' /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: 8 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-08-11T13:51:38+00:00' updated_at: '2026-08-11T13:51:38+00:00' properties: object: type: string example: webhook_endpoint id: type: integer example: 8 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-08-11T13:51:38+00:00' updated_at: type: string example: '2026-08-11T13:51:38+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: 9 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-08-11T13:51:38+00:00' updated_at: '2026-08-11T13:51:38+00:00' properties: object: type: string example: webhook_endpoint id: type: integer example: 9 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-08-11T13:51:38+00:00' updated_at: type: string example: '2026-08-11T13:51:38+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 /api/v1/health: get: summary: 'Liveness probe' operationId: livenessProbe description: "Returns `200 {\"status\":\"ok\"}` whenever the API process is up and serving\ntraffic. It requires no authentication and touches no datastore, so it\nlets you tell apart two failure modes that otherwise look identical: a\n`200` here next to a `5xx` on an authenticated call means the outage is\ndownstream (database / cache), not your API key; no response at all means\nthe service itself is unreachable." parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: status: ok properties: status: type: string example: ok tags: - Health security: []