Introduction
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.
Welcome to the ProxyOn API documentation. ProxyOn orchestrates Stripe Billing so your application can offer subscription management under its own brand. Card data is collected and updated by Stripe; ProxyOn hosts the rest of the billing experience.
Quick Start
Integrate ProxyOn in four base calls — subscriptions are managed entirely inside the ProxyOn-hosted whitelabel billing portal, so you never build payment or plan-management UI yourself:
- Upsert a subscriber when a user signs up:
POST /v1/subscribers. - Open the billing portal for self-service plan selection, changes and cancellation:
POST /v1/portal-sessions→ redirect the user to the returnedurl. - Read entitlements to gate features and enforce limits:
GET /v1/subscribers/{external_id}/entitlements. - Listen for webhooks (
subscription.*,entitlement.updated,invoice.*) to keep your app in sync.
Report metered usage with POST /v1/usage when you bill by consumption.
Base URL
- Production:
https://your-domain.com/api/v1 - Development:
https://proxyon.test/api/v1
Authentication
All API requests require authentication using your Project API Key (pxn_test_* or pxn_live_*). Include it in the Authorization header as a Bearer token.
Rate Limits
- Read operations: 600 requests/minute
- Write operations: 120 requests/minute
- Usage reporting (
proxyon-usagebucket): 1200 requests/minute - Portal sessions (
proxyon-portalbucket): 60 requests/minute
Idempotency
Use Idempotency-Key header for safe retries on write operations. Successful (2xx) and client-error (4xx) responses are cached for 24 hours and replayed on retry; only server errors (5xx) bypass the cache.
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Send the project API key as Bearer pxntest… or Bearer pxnlive…, or use the X-Project-Api-Key header with the same value.
Subscribers
Manage subscribers in your project
List subscribers
requires authentication
Returns the most recent subscribers in your project (up to 100). Use the
email filter to look one up by address, or type to narrow the list to
users or organizations. Cursor pagination is not exposed yet — pivot
bootstrap migrations should rely on these filters.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscribers?type=organization&email=user%40example.com" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers"
);
const params = {
"type": "organization",
"email": "user@example.com",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, List wrapper. `data[]` items follow the Subscriber resource shape.):
{
"object": "list",
"data": [
{
"object": "subscriber",
"id": "sbr_01HXYZ",
"external_id": "user_12345",
"type": "user",
"email": "user@example.com",
"name": "John Doe",
"metadata": [],
"created_at": "2026-05-20T10:00:00+00:00",
"updated_at": "2026-05-20T10:00:00+00:00"
}
]
}
Example response (403, Forbidden — API key lacks the required scope `subscribers:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscribers:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Retrieve a subscriber
requires authentication
Returns the subscriber identified by your application's external_id.
Useful to verify Proxyon has the latest profile information or to fetch
the linked Stripe customer id.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscribers/architecto" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"object": "subscriber",
"id": "sbr_t3wwu9tawndnbcfv12mm",
"external_id": "usr_vhq7hkqioymi",
"type": "user",
"email": "price.amber@example.org",
"name": "Miss Jazlyn Keebler III",
"metadata": null,
"created_at": "2026-06-19T07:30:19+00:00",
"updated_at": "2026-06-19T07:30:19+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscribers:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscribers:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create or update a subscriber
requires authentication
Idempotently upserts a subscriber by external_id. Call this right after
a customer signs up in your application — Proxyon will create the
subscriber on Stripe if needed and mirror the identity locally. Pass an
Idempotency-Key header to safely retry on network errors.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/subscribers" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"external_id\": \"user_12345\",
\"type\": \"user\",
\"email\": \"user@example.com\",
\"name\": \"John Doe\",
\"metadata\": {
\"plan\": \"premium\"
}
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"external_id": "user_12345",
"type": "user",
"email": "user@example.com",
"name": "John Doe",
"metadata": {
"plan": "premium"
}
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"object": "subscriber",
"id": "sbr_ukmzqdw4upzrxqrieddd",
"external_id": "usr_tiy9jkm83crh",
"type": "user",
"email": "okon.justina@example.com",
"name": "Misael Runte",
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscribers:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscribers:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update a subscriber
requires authentication
Partially updates a subscriber. Only the fields you send are changed; existing email, type, name and metadata are preserved. Stripe is reconciled automatically.
Example request:
curl --request PATCH \
"https://proxyon.teknoza.be/api/v1/subscribers/architecto" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"type\": \"organization\",
\"email\": \"newemail@example.com\",
\"name\": \"Jane Doe\",
\"metadata\": {
\"plan\": \"basic\"
}
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"type": "organization",
"email": "newemail@example.com",
"name": "Jane Doe",
"metadata": {
"plan": "basic"
}
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"object": "subscriber",
"id": "sbr_0rivd5fy51dq5eqclgai",
"external_id": "usr_n3rtfkzsyqlj",
"type": "user",
"email": "lafayette.considine@example.com",
"name": "Mr. Adriel Romaguera",
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscribers:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscribers:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete a subscriber
requires authentication
Soft-deletes a subscriber. Active subscriptions are cancelled at the period end by Stripe; the local row is kept for historic invoice access.
Example request:
curl --request DELETE \
"https://proxyon.teknoza.be/api/v1/subscribers/architecto" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/architecto"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204, Subscriber soft-deleted.):
Empty response
Example response (403, Forbidden — API key lacks the required scope `subscribers:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscribers:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Subscriptions
Subscription lifecycle and listing
List a subscriber's subscriptions
requires authentication
Returns every subscription for the subscriber, active ones first. Use this to display a billing history page or to find the subscription id to cancel, swap or resume.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscribers/user_12345/subscriptions" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/user_12345/subscriptions"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, List of the subscriber's subscriptions, active subscriptions first. `data[]` items follow the Subscription resource shape.):
{
"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"
}
]
}
Example response (403, Forbidden — API key lacks the required scope `subscriptions:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscriptions:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Retrieve a subscription
requires authentication
Returns the subscription identified by its public id (sub_*) or numeric
id. Includes the resolved plan, prices and current billing period bounds.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY..." \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY..."
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"object": "subscription",
"id": "sub_vaxvvpaa0rvhe5qlpzwy",
"project_id": "3",
"subscriber_id": "2",
"plan_id": "4",
"status": "active",
"current_period_start": "2026-06-01T00:00:00+00:00",
"current_period_end": "2026-07-01T00:00:00+00:00",
"trial_ends_at": null,
"cancel_at": null,
"canceled_at": null,
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscriptions:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscriptions:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Cancel a subscription
requires authentication
Cancels a subscription. By default it stays active until the end of the
current billing period; pass at_period_end=false to cancel immediately
and prorate the unused time.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY.../cancel" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"at_period_end\": true,
\"reason\": \"Customer requested downgrade\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY.../cancel"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"at_period_end": true,
"reason": "Customer requested downgrade"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"object": "subscription",
"id": "sub_t2iwx7fs0gxsz2xwa7gd",
"project_id": "13",
"subscriber_id": "6",
"plan_id": "7",
"status": "active",
"current_period_start": "2026-06-01T00:00:00+00:00",
"current_period_end": "2026-07-01T00:00:00+00:00",
"trial_ends_at": null,
"cancel_at": null,
"canceled_at": null,
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscriptions:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscriptions:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Example response (422, Domain error: `subscription_already_canceled`.):
{
"error": {
"type": "subscription_already_canceled",
"message": "Subscription is already canceled.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_already_canceled"
}
}
Example response (422, Domain error: `subscription_not_actionable_cancel`.):
{
"error": {
"type": "subscription_not_actionable_cancel",
"message": "Subscription state does not allow cancellation.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_not_actionable_cancel"
}
}
Example response (422, Domain error: `subscription_missing_stripe_id`.):
{
"error": {
"type": "subscription_missing_stripe_id",
"message": "Subscription is missing its Stripe identifier.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_missing_stripe_id"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Swap a subscription's plan price
requires authentication
Switches the subscription to a different plan_price_id. Stripe handles
proration automatically — pass proration="none" to skip it and bill
the new price starting from the next cycle.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY.../swap" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"plan_price_id\": 42,
\"proration\": \"always\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY.../swap"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"plan_price_id": 42,
"proration": "always"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"object": "subscription",
"id": "sub_ekutbdkjle1uqdrqjfoi",
"project_id": "16",
"subscriber_id": "7",
"plan_id": "8",
"status": "active",
"current_period_start": "2026-06-01T00:00:00+00:00",
"current_period_end": "2026-07-01T00:00:00+00:00",
"trial_ends_at": null,
"cancel_at": null,
"canceled_at": null,
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscriptions:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscriptions:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Example response (422, Domain error: `subscription_not_actionable_swap`.):
{
"error": {
"type": "subscription_not_actionable_swap",
"message": "Subscription state does not allow swapping.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_not_actionable_swap"
}
}
Example response (422, Domain error: `subscription_missing_stripe_id`.):
{
"error": {
"type": "subscription_missing_stripe_id",
"message": "Subscription is missing its Stripe identifier.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_missing_stripe_id"
}
}
Example response (422, Domain error: `plan_archived`.):
{
"error": {
"type": "plan_archived",
"message": "Plan is archived and cannot be used for new subscriptions.",
"doc_url": "https://proxyon.teknoza.be/docs#plan_archived"
}
}
Example response (422, Domain error: `plan_price_inactive`.):
{
"error": {
"type": "plan_price_inactive",
"message": "Plan price is inactive.",
"doc_url": "https://proxyon.teknoza.be/docs#plan_price_inactive"
}
}
Example response (422, Domain error: `subscription_price_not_synced`.):
{
"error": {
"type": "subscription_price_not_synced",
"message": "Subscription price is not synced to Stripe.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_price_not_synced"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Resume a cancelled subscription
requires authentication
Reverts a subscription that was previously cancelled at period end. Only works while the current billing period has not yet ended; after that the subscription must be re-created.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY.../resume" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscriptions/sub_01JXY.../resume"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200):
{
"object": "subscription",
"id": "sub_ieqmfxekub3v6njrvxfh",
"project_id": "19",
"subscriber_id": "8",
"plan_id": "9",
"status": "active",
"current_period_start": "2026-06-01T00:00:00+00:00",
"current_period_end": "2026-07-01T00:00:00+00:00",
"trial_ends_at": null,
"cancel_at": null,
"canceled_at": null,
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `subscriptions:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: subscriptions:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Example response (422, Domain error: `subscription_already_canceled`.):
{
"error": {
"type": "subscription_already_canceled",
"message": "Subscription is already canceled.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_already_canceled"
}
}
Example response (422, Domain error: `subscription_cannot_resume`.):
{
"error": {
"type": "subscription_cannot_resume",
"message": "Subscription cannot be resumed in its current state.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_cannot_resume"
}
}
Example response (422, Domain error: `subscription_missing_stripe_id`.):
{
"error": {
"type": "subscription_missing_stripe_id",
"message": "Subscription is missing its Stripe identifier.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_missing_stripe_id"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Plans
List and retrieve plans in your project
List plans
requires authentication
Returns the active plans in your project, each with its active prices
and the currency they are billed in. Use this to render a pricing page in
your own UI; subscribers pick and pay for a plan inside the ProxyOn
billing portal (POST /v1/portal-sessions).
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/plans?currency=USD" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/plans"
);
const params = {
"currency": "USD",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, List of active plans. `data[]` items follow the Plan resource shape (with `prices[]`).):
{
"object": "list",
"data": [
{
"object": "plan",
"id": "1",
"key": "pro_monthly",
"locale": "en",
"name": "Pro Monthly",
"description": "Professional plan billed monthly.",
"status": "active",
"interval_unit": "month",
"interval_count": 1,
"trial_days": 14,
"metadata": [],
"created_at": "2026-05-01T00:00:00+00:00",
"updated_at": "2026-05-01T00:00:00+00:00",
"prices": []
}
]
}
Example response (403, Forbidden — API key lacks the required scope `plans:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: plans:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Retrieve a plan by key
requires authentication
Returns a single plan and its active prices, identified by the
human-readable plan key (e.g. pro, basic-monthly). Prefer this over
numeric ids when wiring up your pricing UI.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/plans/basic-monthly" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/plans/basic-monthly"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"object": "plan",
"id": "5",
"key": "aut_adipisci",
"locale": "en",
"name": "Quidem nostrum Plan",
"description": "",
"status": "draft",
"metadata": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `plans:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: plans:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Portal
Whitelabel billing portal sessions
Create a billing portal session
requires authentication
Issues a single-use magic link to the project's whitelabel ProxyOn
billing portal so the subscriber can choose or change a plan, update their
payment method, view invoices and cancel on their own. Redirect them to
the returned url; when they leave the portal they are sent back to
return_url.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/portal-sessions" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"subscriber_external_id\": \"user_12345\",
\"return_url\": \"https:\\/\\/app.example.com\\/account\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/portal-sessions"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"subscriber_external_id": "user_12345",
"return_url": "https:\/\/app.example.com\/account"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Portal session created. Redirect the subscriber to the returned URL.):
{
"url": "https://billing.example.com/portal/abcdef0123456789",
"expires_at": "2026-01-15T10:15:00+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `portal:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: portal:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Domain error: `subscriber_not_found`.):
{
"error": {
"type": "subscriber_not_found",
"message": "Subscriber not found for this project.",
"doc_url": "https://proxyon.teknoza.be/docs#subscriber_not_found"
}
}
Example response (409, Domain error: `stripe_not_connected`.):
{
"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"
}
}
Example response (422, Domain error: `subscriber_wrong_project`.):
{
"error": {
"type": "subscriber_wrong_project",
"message": "Subscriber does not belong to the authenticated project.",
"doc_url": "https://proxyon.teknoza.be/docs#subscriber_wrong_project"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Entitlements
Resolve subscriber feature entitlements
Resolve a subscriber's entitlements
requires authentication
Returns the resolved feature flags and limits the subscriber currently
has access to, based on their active subscription's plan. Cache this on
the client for 60 seconds — Proxyon returns a strong ETag so you can
revalidate with If-None-Match for a cheap 304 Not Modified.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscribers/user_12345/entitlements" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "If-None-Match: \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/user_12345/entitlements"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"If-None-Match": ""e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, 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.):
{
"object": "entitlements",
"data": {
"subscriber_id": 42,
"project_id": 7,
"subscription_id": 311,
"plan": {
"key": "pro",
"name": "Pro",
"description": "Professional plan.",
"interval_unit": "month",
"interval_count": 1
},
"seats": 5,
"entries": [
{
"key": "projects",
"type": "quota",
"value": 5,
"remaining": 3,
"period_start": "2026-06-01T00:00:00+00:00",
"period_end": "2026-07-01T00:00:00+00:00"
},
{
"key": "sso",
"type": "boolean",
"value": true,
"remaining": null,
"period_start": "2026-06-01T00:00:00+00:00",
"period_end": "2026-07-01T00:00:00+00:00"
}
],
"generated_at": "2026-06-10T12:00:00+00:00"
}
}
Example response (304, Entitlement set unchanged since the ETag in `If-None-Match`.):
Example response (403, Forbidden — API key lacks the required scope `entitlements:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: entitlements:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Usage
Metered usage records and summaries
Get a subscriber's usage summary
requires authentication
Aggregates the subscriber's recorded usage for a single metered feature
within the current billing period. Returns 404 no_active_subscription
when the subscriber has no active subscription to report against.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscribers/user_12345/usage?feature_key=api_calls&period=current" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"feature_key\": \"b\",
\"period\": \"last\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/user_12345/usage"
);
const params = {
"feature_key": "api_calls",
"period": "current",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"feature_key": "b",
"period": "last"
};
fetch(url, {
method: "GET",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200, Aggregated usage for the subscriber's active subscription, scoped to the current billing period.):
{
"object": "usage_summary",
"feature_key": "api_calls",
"quantity": 1284,
"period_start": "2026-05-01T00:00:00+00:00",
"period_end": "2026-06-01T00:00:00+00:00",
"subscription_public_id": "sub_01JXY..."
}
Example response (403, Forbidden — API key lacks the required scope `usage:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: usage:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Subscriber has no active subscription to summarise usage against.):
{
"error": {
"type": "no_active_subscription",
"message": "Subscriber has no active subscription.",
"doc_url": "/docs#no_active_subscription"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Record metered usage
requires authentication
Records a usage event against the subscriber's active metered subscription item. Use this whenever the subscriber consumes a billable feature (API call, transcoded video, sent email, …).
Pass a stable idempotency_key per logical event — the same key always
returns the same record, so it is safe to retry on network errors. The
recorded quantity is forwarded to Stripe and reflected in the next
invoice for the subscriber's current billing period.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/usage" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"subscriber_external_id\": \"user_12345\",
\"feature_key\": \"api_calls\",
\"quantity\": 10,
\"idempotency_key\": \"01JXY7QGJ8KZ...\",
\"recorded_at\": \"2026-01-15T10:00:00Z\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/usage"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"subscriber_external_id": "user_12345",
"feature_key": "api_calls",
"quantity": 10,
"idempotency_key": "01JXY7QGJ8KZ...",
"recorded_at": "2026-01-15T10:00:00Z"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"object": "usage_record",
"id": "1",
"feature_id": "6",
"quantity": 7,
"recorded_at": "2026-06-19T07:30:20+00:00",
"idempotency_key": "usage_jabpoyoei97ev8uh4h25yfch",
"created_at": "2026-06-19T07:30:21+00:00",
"updated_at": "2026-06-19T07:30:21+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `usage:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: usage:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Domain error: `no_active_subscription`.):
{
"error": {
"type": "no_active_subscription",
"message": "This subscriber has no active subscription.",
"doc_url": "https://proxyon.teknoza.be/docs#no_active_subscription"
}
}
Example response (422, Domain error: `usage_invalid_quantity`.):
{
"error": {
"type": "usage_invalid_quantity",
"message": "Usage quantity must be a positive integer.",
"doc_url": "https://proxyon.teknoza.be/docs#usage_invalid_quantity"
}
}
Example response (422, Domain error: `usage_correction_requires_sum_aggregation`.):
{
"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"
}
}
Example response (422, Domain error: `usage_recorded_at_in_future`.):
{
"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"
}
}
Example response (422, Domain error: `usage_recorded_at_too_old`.):
{
"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"
}
}
Example response (422, Domain error: `usage_subscription_not_active`.):
{
"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"
}
}
Example response (422, Domain error: `usage_unsupported_feature_type`.):
{
"error": {
"type": "usage_unsupported_feature_type",
"message": "Feature type does not support usage records.",
"doc_url": "https://proxyon.teknoza.be/docs#usage_unsupported_feature_type"
}
}
Example response (422, Domain error: `usage_feature_not_in_plan`.):
{
"error": {
"type": "usage_feature_not_in_plan",
"message": "Feature is not part of the subscription plan.",
"doc_url": "https://proxyon.teknoza.be/docs#usage_feature_not_in_plan"
}
}
Example response (422, Domain error: `subscription_no_items`.):
{
"error": {
"type": "subscription_no_items",
"message": "Subscription has no priced items.",
"doc_url": "https://proxyon.teknoza.be/docs#subscription_no_items"
}
}
Example response (422, Domain error: `plan_cross_project_resource`.):
{
"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"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Invoices
List and retrieve invoices
List a subscriber's invoices
requires authentication
Returns invoices for the subscriber in reverse chronological order, including hosted invoice URLs and PDF links generated by Stripe. Ideal for a "Billing history" tab in your application.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/subscribers/user_12345/invoices" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/subscribers/user_12345/invoices"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, List of invoices for the subscriber, newest first. `data[]` items follow the Invoice resource shape.):
{
"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"
}
]
}
Example response (403, Forbidden — API key lacks the required scope `invoices:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: invoices:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Retrieve an invoice
requires authentication
Returns a single invoice by its public id (in_*) or numeric id,
including the hosted Stripe URL and PDF download link.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/invoices/in_01JXY..." \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/invoices/in_01JXY..."
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"object": "invoice",
"id": "in_zqkfw2oyq3hruqprcz4g",
"subscription_id": "2",
"currency_id": "1",
"status": "open",
"amount_due": 49055,
"amount_paid": 0,
"hosted_invoice_url": null,
"pdf_url": null,
"period_start": "2026-06-01T00:00:00+00:00",
"period_end": "2026-07-01T00:00:00+00:00",
"paid_at": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `invoices:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: invoices:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Webhook Endpoints
Manage outbound webhook endpoints for your project
List webhook endpoints
requires authentication
Returns every webhook endpoint configured for your project, with their current status and which event types they subscribe to. Use this to render a webhooks settings page in your dashboard.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/webhook-endpoints" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/webhook-endpoints"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200, List of webhook endpoints. `data[]` items follow the WebhookEndpoint resource shape.):
{
"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"
}
]
}
Example response (403, Forbidden — API key lacks the required scope `webhooks:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: webhooks:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Retrieve a webhook endpoint
requires authentication
Returns a single webhook endpoint, including its current delivery status and last-success / failure counters.
Example request:
curl --request GET \
--get "https://proxyon.teknoza.be/api/v1/webhook-endpoints/16" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());Example response (200):
{
"object": "webhook_endpoint",
"id": 1,
"url": "https://example.com/webhooks/bfc53181-d647-36b2-9080-f9c2b76006f4",
"description": "Qui commodi incidunt iure odit.",
"status": "active",
"event_types": [],
"consecutive_failures": 0,
"last_success_at": null,
"last_failure_at": null,
"signing_secret_grace_ends_at": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `webhooks:read`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: webhooks:read",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create a webhook endpoint
requires authentication
Registers a new HTTPS URL to receive Proxyon webhook deliveries. The
response contains a one-time-visible signing secret — store it
securely on your server and use it to verify the Proxyon-Signature
header on every delivery.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/webhook-endpoints" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"url\": \"https:\\/\\/app.example.com\\/webhooks\\/proxyon\",
\"event_types\": [
\"subscription.created\",
\"invoice.paid\"
],
\"description\": \"Production receiver\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/webhook-endpoints"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "https:\/\/app.example.com\/webhooks\/proxyon",
"event_types": [
"subscription.created",
"invoice.paid"
],
"description": "Production receiver"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (201, Webhook endpoint created. `signing_secret` is exposed **once** — store it securely; subsequent reads of this endpoint will omit it.):
{
"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"
}
Example response (403, Forbidden — API key lacks the required scope `webhooks:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: webhooks:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update a webhook endpoint
requires authentication
Updates the URL, description, subscribed events or active status of a
webhook endpoint. Setting status="disabled" pauses deliveries without
losing the signing secret.
Example request:
curl --request PATCH \
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"url\": \"http:\\/\\/www.bailey.biz\\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html\",
\"event_types\": [
\"architecto\"
],
\"description\": \"Eius et animi quos velit et.\",
\"status\": \"architecto\"
}"
const url = new URL(
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"url": "http:\/\/www.bailey.biz\/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html",
"event_types": [
"architecto"
],
"description": "Eius et animi quos velit et.",
"status": "architecto"
};
fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify(body),
}).then(response => response.json());Example response (200):
{
"object": "webhook_endpoint",
"id": 2,
"url": "https://example.com/webhooks/a4855dc5-0acb-33c3-b921-f4291f719ca0",
"description": null,
"status": "active",
"event_types": [],
"consecutive_failures": 0,
"last_success_at": null,
"last_failure_at": null,
"signing_secret_grace_ends_at": null,
"created_at": "2026-06-19T07:30:20+00:00",
"updated_at": "2026-06-19T07:30:20+00:00"
}
Example response (403, Forbidden — API key lacks the required scope `webhooks:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: webhooks:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete a webhook endpoint
requires authentication
Permanently removes a webhook endpoint. In-flight deliveries are aborted; pending retries are discarded.
Example request:
curl --request DELETE \
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());Example response (204, Webhook endpoint deleted.):
Empty response
Example response (403, Forbidden — API key lacks the required scope `webhooks:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: webhooks:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Rotate the signing secret
requires authentication
Generates a new signing secret for the webhook endpoint and returns it
in the response (one-time view). The previous secret continues to sign
deliveries for a 24-hour grace period so you can roll out the new one
without dropping events.
Example request:
curl --request POST \
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16/rotate" \
--header "Authorization: Bearer {YOUR_AUTH_KEY}" \
--header "Idempotency-Key: idem_01JXY..." \
--header "Content-Type: application/json" \
--header "Accept: application/json"const url = new URL(
"https://proxyon.teknoza.be/api/v1/webhook-endpoints/16/rotate"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Idempotency-Key": "idem_01JXY...",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());Example response (200, 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`).):
{
"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"
}
Example response (403, Forbidden — API key lacks the required scope `webhooks:write`.):
{
"error": {
"type": "insufficient_scope",
"message": "API key lacks required scope: webhooks:write",
"doc_url": "https://proxyon.teknoza.be/docs#insufficient_scope"
}
}
Example response (404, Not Found — the referenced resource does not exist for this project.):
{
"error": {
"type": "not_found",
"message": "The requested resource was not found.",
"doc_url": "https://proxyon.teknoza.be/docs#not_found"
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.