Billing and usage
Operations tagged Billing and usage in the public API.
About Billing and usage
Read-only billing: what the account's plan grants, what it has used, and what it was charged. Changing the plan, buying add-ons or credit packs, and the Stripe billing portal stay in the dashboard — they end on Stripe-hosted pages only a person can complete — so there is no billing:write scope.
Message credits. Chat replies spend message credits. The plan grants a monthly allowance (credits.limit, null for unlimited): on a paid plan the month follows the billing cycle, on Free the calendar month. Purchased credits (credits.balance) never expire and are spent only once the allowance is used up. credits.remaining is what is left of both; at 0 a chat turn is refused with 402 MESSAGE_CREDITS_EXCEEDED. Test keys spend credits exactly like live keys. Subscribe to billing.credits.low and billing.credits.exhausted to be told before and when it happens.
Which call. GET /usage (identical to GET /billing/usage) is the meter to poll: credits and knowledge storage. GET /billing/subscription adds the plan in effect, its status, trial, renewal and cancellation dates, and agent and seat usage. GET /billing/plans is the self-service catalogue, and GET /billing/invoices the account's Stripe invoices.
IDs you will meet.
idon the usage and subscription objects — the account id.- A plan
key(free,starter,standard,pro, …) — how a plan is named everywhere; Stripe price ids are never exposed. - Invoice
id— Stripe's invoice id (in_…), the one string id of this group. No operation takes it.
Retrieve the subscription
status, billing interval, trial, renewal and cancellation dates, a downgrade scheduled for the period end — with usage against every plan limit: message credits, chatbots, knowledge storage and team seats. It reads what the dashboard's billing page reads, so the two never disagree. To poll only the credit meter, GET /usage is smaller.- Required scope
- billing:read
- Idempotency
- Read-only
- Rate limits
Responses
- 200Success.
objectstringRequiredAlways
billing_subscriptionidintegerRequiredThe account id.planobjectRequiredThe plan in effect — paid, complimentary, on trial or Free.statusstringRequiredThe state of the plan in effect:free,complimentary(granted by Agentency),trialing,active,past_due,grace(cancelled, paid up untilends_at),canceled, or another Stripe subscription status (incomplete,incomplete_expired,unpaid,paused).intervalstringnullableRequiredBilling interval of the paid subscription in effect;nullwhen no paid subscription grants the plan (Free, complimentary, ended, paused).Possible values
monthlyyearlynullis_subscribedbooleanRequiredWhether a paid subscription grants the plan in effect.is_compbooleanRequiredWhether the plan in effect is complimentary, granted by Agentency rather than paid for.has_payment_issuebooleanRequiredWhether the Stripe subscription needs payment attention (past_due,unpaid,incompleteorpaused), even while a complimentary plan is in effect.on_trialbooleanRequiredWhether the plan in effect is a trial.trial_ends_atstringnullableRequiredWhen the trial ends (ISO 8601);nullwithout a trial.cancel_at_period_endbooleanRequiredWhether the subscription is cancelled and runs untilends_atwithout renewing.ends_atstringnullableRequiredWhen a cancelled subscription ends (ISO 8601);nullwhile it renews.renews_atstringnullableRequiredNext renewal of anactivepaid subscription (ISO 8601).nullwhile cancelling, trialing, complimentary, or until the first billing period has synced.comp_expires_atstringnullableRequiredWhen a complimentary plan ends (ISO 8601);nullwhen it is permanent or not in effect.pending_changeobjectnullableRequiredA downgrade scheduled for the end of the billing period;nullwhen none. Undone in the dashboard.pending_addon_changesarray of objectRequiredRecurring add-ons whose quantity drops at the end of the billing period; empty when none.usageobjectRequiredUsage against every limit of the plan.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/billing/subscription' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "billing_subscription",
"id": 1,
"plan": {
"key": "free",
"name": "Free",
"is_popular": false,
"contact_sales": false,
"price": {
"monthly": 0,
"yearly": 0
},
"purchasable": {
"monthly": false,
"yearly": false
},
"entitlements": {
"message_credits": 50,
"max_agents": 1,
"kb_bytes_per_agent": 409600,
"ai_actions_per_agent": 0,
"instructions_per_agent": 1,
"integrations_enabled": false,
"website_embed_enabled": true,
"team_seats": null,
"ai_model_selection": false
},
"features": []
},
"status": "free",
"interval": null,
"is_subscribed": false,
"is_comp": false,
"has_payment_issue": false,
"on_trial": false,
"trial_ends_at": null,
"cancel_at_period_end": false,
"ends_at": null,
"renews_at": null,
"comp_expires_at": null,
"pending_change": null,
"pending_addon_changes": [],
"usage": {
"credits": {
"used": 0,
"limit": 50,
"balance": 0,
"remaining": 50
},
"agents": {
"used": 2,
"limit": 1,
"extra": 0
},
"knowledge": {
"per_agent_limit": 409600,
"pool_limit": 0,
"pool_used": 0,
"extra_packs": 0
},
"seats": {
"used": 1,
"limit": null
}
}
}List plans
key, display price, the billing intervals a checkout accepts, its limits (entitlements) and feature keys. Plans are bought in the dashboard; Stripe price ids are never exposed.- Required scope
- billing:read
- Idempotency
- Read-only
- Rate limits
Responses
- 200Success.
objectstringRequiredAlways
listdataarray of PlanResourceRequiredhas_morebooleanRequirednext_cursoranynullableRequiredprevious_cursoranynullableRequiredurlstringRequiredAlways
/v1/billing/plans
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/billing/plans' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "list",
"data": [
{
"object": "plan",
"key": "free",
"name": "Free",
"is_popular": false,
"is_free": true,
"contact_sales": false,
"price": {
"monthly": 0,
"yearly": 0
},
"purchasable_intervals": [],
"entitlements": {
"message_credits": 50,
"max_agents": 1,
"kb_bytes_per_agent": 409600,
"ai_actions_per_agent": 0,
"instructions_per_agent": 1,
"integrations_enabled": false,
"website_embed_enabled": true,
"team_seats": null,
"ai_model_selection": false
},
"features": []
},
{
"object": "plan",
"key": "starter",
"name": "Starter",
"is_popular": false,
"is_free": false,
"contact_sales": false,
"price": {
"monthly": 19,
"yearly": 182
},
"purchasable_intervals": [],
"entitlements": {
"message_credits": 500,
"max_agents": 2,
"kb_bytes_per_agent": 10485760,
"ai_actions_per_agent": 3,
"instructions_per_agent": 3,
"integrations_enabled": true,
"website_embed_enabled": true,
"team_seats": null,
"ai_model_selection": true
},
"features": [
"everything_free"
]
},
{
"object": "plan",
"key": "standard",
"name": "Standard",
"is_popular": true,
"is_free": false,
"contact_sales": false,
"price": {
"monthly": 99,
"yearly": 950
},
"purchasable_intervals": [],
"entitlements": {
"message_credits": 4000,
"max_agents": 5,
"kb_bytes_per_agent": 20971520,
"ai_actions_per_agent": 8,
"instructions_per_agent": 8,
"integrations_enabled": true,
"website_embed_enabled": true,
"team_seats": null,
"ai_model_selection": true
},
"features": [
"everything_starter"
]
},
{
"object": "plan",
"key": "pro",
"name": "Pro",
"is_popular": false,
"is_free": false,
"contact_sales": false,
"price": {
"monthly": 249,
"yearly": 2390
},
"purchasable_intervals": [],
"entitlements": {
"message_credits": 15000,
"max_agents": 10,
"kb_bytes_per_agent": 41943040,
"ai_actions_per_agent": 12,
"instructions_per_agent": 12,
"integrations_enabled": true,
"website_embed_enabled": true,
"team_seats": null,
"ai_model_selection": true
},
"features": [
"everything_standard"
]
},
{
"object": "plan",
"key": "agency",
"name": "Agency",
"is_popular": false,
"is_free": false,
"contact_sales": false,
"price": {
"monthly": 499,
"yearly": 4790
},
"purchasable_intervals": [],
"entitlements": {
"message_credits": 50000,
"max_agents": 30,
"kb_bytes_per_agent": 62914560,
"ai_actions_per_agent": 20,
"instructions_per_agent": 20,
"integrations_enabled": true,
"website_embed_enabled": true,
"team_seats": null,
"ai_model_selection": true
},
"features": [
"everything_pro"
]
}
],
"has_more": false,
"next_cursor": null,
"previous_cursor": null,
"url": "/v1/billing/plans"
}Retrieve usage
GET /usage returns the same body at a shorter path.- Required scope
- billing:read
- Idempotency
- Read-only
- Rate limits
Responses
- 200Success.
objectstringRequiredAlways
billing_usageidintegerRequiredThe account id.planstringRequiredThe key of the plan in effect (seeGET /billing/plans).creditsobjectRequiredMessage credits this month.knowledgeobjectRequiredKnowledge storage, counted in characters (1 character = 1 byte).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/billing/usage' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "billing_usage",
"id": 1,
"plan": "free",
"credits": {
"used": 0,
"limit": 50,
"balance": 0,
"remaining": 50
},
"knowledge": {
"per_agent_limit": 409600,
"pool_limit": 0,
"pool_used": 0,
"extra_packs": 0
}
}List invoices
limit, starting_after, ending_before); every status is included — filter on status client-side for paid ones. An account without a Stripe customer, or whose customer the billing account no longer knows, has an empty list. Each page is cached for a minute by default; a Stripe outage is 500 SERVER_ERROR, never an empty list.- Required scope
- billing:read
- Idempotency
- Read-only
- Rate limits
Query parameters
limitintegerOptionalPage size, clamped to 1–100.Default:
20Minimum:1Maximum:100starting_afterstringOptionalCursor from a previous page'snext_cursor: returns the page after it. A cursor minted for another list,sortororderis 422INVALID_CURSOR.ending_beforestringOptionalCursor from a previous page'sprevious_cursor: returns the page before it. Wins when both cursors are sent.
Responses
- 200Success.
objectstringRequiredAlways
listdataarray of InvoiceResourceRequiredhas_morebooleanRequirednext_cursorstringnullableRequiredprevious_cursorstringnullableRequiredurlstringRequiredAlways
/v1/billing/invoices
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 422Validation failed. See
error.details.Error codesINVALID_CURSOR
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/billing/invoices' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "list",
"data": [
{
"object": "invoice",
"id": "in_1QhExampleOpenInvoice",
"total": "$19.00",
"status": "open"
},
{
"object": "invoice",
"id": "in_1QfExamplePaidInvoice",
"total": "$19.00",
"status": "paid"
}
],
"has_more": false,
"next_cursor": null,
"previous_cursor": null,
"url": "/v1/billing/invoices"
}Retrieve usage at the short path
GET /billing/usage, at a top-level path: message credits used and left this month, and knowledge storage. Poll it to warn before credits run out, or subscribe to billing.credits.low and billing.credits.exhausted instead.- Required scope
- billing:read
- Idempotency
- Read-only
- Rate limits
Responses
- 200Success.
objectstringRequiredAlways
billing_usageidintegerRequiredThe account id.planstringRequiredThe key of the plan in effect (seeGET /billing/plans).creditsobjectRequiredMessage credits this month.knowledgeobjectRequiredKnowledge storage, counted in characters (1 character = 1 byte).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/usage' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "billing_usage",
"id": 1,
"plan": "free",
"credits": {
"used": 0,
"limit": 50,
"balance": 0,
"remaining": 50
},
"knowledge": {
"per_agent_limit": 409600,
"pool_limit": 0,
"pool_used": 0,
"extra_packs": 0
}
}