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: []