Billing
Agents can read the account’s plan and billing status, and can ask their human to upgrade. Agents cannot pay, cancel, or change payment methods — only a human can, in the console, through a secure checkout.
When you hit a plan limit
Section titled “When you hit a plan limit”- Don’t retry in a loop. A
403 plan_requiredor a429 quota_exceededwill not clear by retrying. - Call
GET /v1/billingto see the current plan and any request already pending. - Call
POST /v1/billing/upgrade-requeststo create an upgrade request. Pass thefeatureormetricfrom the error so your human sees what was blocked. - Give the returned
approval_urlto your human. - Poll
GET /v1/billing/upgrade-requests/{id}at most once a minute untilstatusis no longerpending. - After
status: completed, retry the original call about 15 seconds later.
See Error Codes for the error bodies that carry upgrade_url.
Get billing
Section titled “Get billing”GET /v1/billingReturns a read-only billing summary for the organization, including any pending upgrade request.
curl https://api.chronary.ai/v1/billing \ -H "Authorization: Bearer chr_sk_your_key_here"import { Chronary } from '@chronary/sdk';
const client = new Chronary();const billing = await client.billing.get();console.log(billing.plan, billing.self_serve_upgrade);from chronary import Chronary
client = Chronary()billing = client.billing.get()print(billing.plan, billing.self_serve_upgrade)chronary billingResponse 200 OK
Section titled “Response 200 OK”{ "plan": "free-agent", "plan_source": null, "plan_expires_at": null, "status": null, "interval": null, "current_period_end": null, "cancel_at": null, "cancel_at_period_end": false, "renews_at": null, "price": null, "has_billing_account": false, "self_serve_upgrade": true, "upgrade_options": [ { "plan": "pro", "interval": "month", "amount": 2900, "currency": "usd", "savings_vs_monthly": 0 }, { "plan": "pro", "interval": "year", "amount": 29000, "currency": "usd", "savings_vs_monthly": 5800 } ], "upgrade_url": "https://console.chronary.ai/settings/billing", "manage_url": "https://console.chronary.ai/settings/billing", "contact_url": null, "pending_upgrade_request": null, "offer": null}Response fields
Section titled “Response fields”All amounts are in minor units (2900 = $29.00) and exclude tax.
| Field | Description |
|---|---|
plan | Current plan, for example free, pro, or scale |
plan_source | How the plan was granted, or null |
plan_expires_at | ISO-8601 end of a time-limited plan grant, or null |
status | Subscription status; null if the account never subscribed |
interval | month or year, or null |
current_period_end | ISO-8601 end of the current paid period, or null |
cancel_at | ISO-8601 time a scheduled cancellation takes effect, or null |
cancel_at_period_end | true when a cancellation is scheduled |
renews_at | ISO-8601 next renewal, or null when no renewal is expected |
price | { amount, currency, interval, tax_exclusive } for the active subscription, or null |
has_billing_account | true once the account has a billing profile (invoices and payment methods live in the console) |
self_serve_upgrade | true when a human can start a secure checkout right now |
upgrade_options | Plans a human can upgrade to; empty when self_serve_upgrade is false |
upgrade_url | Console link for a human to upgrade, or null |
manage_url | Console billing page |
contact_url | Contact link for plans that need a conversation (Scale), or null |
pending_upgrade_request | The pending upgrade request, or null |
offer | The founding member offer for early-access accounts, or null |
Founding member offer
Section titled “Founding member offer”Accounts that joined during early access become founding members at launch: Pro stays free for at least 60 days after launch, and they can subscribe before then at a discount. Billing doesn’t start until the free period ends.
{ "kind": "founding", "status": "available", "ends_at": "2027-02-01T00:00:00.000Z", "trial_until": "2027-02-01T00:00:00.000Z", "discount_percent": 50, "discount_months": 12, "renewal_consent": null}| Field | Description |
|---|---|
kind | Always founding |
status | upcoming — early-access account before launch; the offer opens at launch, and checkout and upgrade requests return 409 founding_offer_pending until then. available — free Pro until ends_at; subscribe before then. redeemed — the subscription was created on founding terms |
ends_at | available: when free Pro and the offer end. Otherwise null |
trial_until | available: billing starts on this date if your human subscribes now (null in the last two days, when billing starts at checkout). redeemed: the end of the free period while it lasts |
discount_percent | Discount on the founding terms, or null when none applies |
discount_months | How many paid months the discount covers (annual plans: the first year), or null |
renewal_consent | redeemed only. required while a free period with a Washington DC billing address waits for your human to choose Continue to paid Pro on the console Billing page. If they don’t, the subscription ends when the free period does, with no charge. given once they have. Otherwise null. Agents can’t give this consent; hand manage_url to your human |
When status is available and the account is about to need Pro features, give manage_url to your human — subscribing early costs nothing.
Create an upgrade request
Section titled “Create an upgrade request”POST /v1/billing/upgrade-requestsAsks your human to upgrade the account to Pro. Returns an approval_url to give to your human. Honors the Idempotency-Key header. If a request is already pending, the same request comes back with reused: true and status 200 — don’t call again, poll it.
Requires an org-level API key.
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
interval | string | No | month (default) or year |
reason | string (max 500) | No | Why the upgrade is needed. Shown to your human as plain text |
feature | string | No | The plan-gated feature that was blocked, for example scoped_keys |
metric | string | No | The quota metric that was exhausted, for example api_calls |
curl -X POST https://api.chronary.ai/v1/billing/upgrade-requests \ -H "Authorization: Bearer chr_sk_your_key_here" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"interval": "month", "reason": "Need scoped keys for my sub-agents", "feature": "scoped_keys"}'const request = await client.billing.upgradeRequests.create( { interval: 'month', reason: 'Need scoped keys for my sub-agents', feature: 'scoped_keys' }, { idempotencyKey: 'upgrade-scoped-keys-1' },);console.log(request.approval_url); // give this to your humanrequest = client.billing.upgrade_requests.create( interval="month", reason="Need scoped keys for my sub-agents", feature="scoped_keys", idempotency_key="upgrade-scoped-keys-1",)print(request.approval_url) # give this to your humanchronary billing request-upgrade --interval month --reason "Need scoped keys for my sub-agents" --waitResponse 201 Created
Section titled “Response 201 Created”200 OK with reused: true when a request is already pending.
{ "id": "upr_abc123", "status": "pending", "plan": "pro", "interval": "month", "reason": "Need scoped keys for my sub-agents", "feature": "scoped_keys", "metric": null, "requested_via": "api", "requested_by_key_prefix": "chr_sk_abcdefghijkl", "approval_url": "https://console.chronary.ai/settings/billing/requests/upr_abc123", "created_at": "2026-10-19T00:00:00.000Z", "expires_at": "2026-10-26T00:00:00.000Z", "completed_at": null, "declined_at": null, "canceled_at": null, "owner_notification": "queued", "reused": false}Errors
Section titled “Errors”| Status | type | Meaning |
|---|---|---|
| 400 | validation_error | Invalid body (for example interval not month or year, or reason over 500 characters) |
| 403 | forbidden | Agent-scoped key; use an org-level key |
| 403 | use_billing_page | Called from a console session; use the billing page instead |
| 403 | email_verification_required | The account’s email isn’t verified yet |
| 409 | already_subscribed | The account already has an active subscription |
| 409 | unsupported_plan | The current plan can’t self-serve upgrade; the error includes contact_url |
| 409 | founding_offer_pending | Early-access account before launch — Pro is still free, and the founding member offer opens at launch. Don’t ask your human to pay yet |
| 409 | upgrade_request_recently_declined | A human recently declined; wait retry_after seconds before asking again |
| 429 | upgrade_request_limit | Too many requests; wait retry_after seconds |
| 503 | billing_not_configured | Billing isn’t available in this environment |
Get an upgrade request
Section titled “Get an upgrade request”GET /v1/billing/upgrade-requests/{id}Poll at most once a minute. When status leaves pending, stop polling.
curl https://api.chronary.ai/v1/billing/upgrade-requests/upr_abc123 \ -H "Authorization: Bearer chr_sk_your_key_here"const request = await client.billing.upgradeRequests.get('upr_abc123');request = client.billing.upgrade_requests.get("upr_abc123")chronary billing upgrade-request get upr_abc123Returns the upgrade request object, or 404 not_found.
Cancel an upgrade request
Section titled “Cancel an upgrade request”POST /v1/billing/upgrade-requests/{id}/cancelWithdraws a pending request; its approval_url stops working. Returns the request with status: "canceled". Fails with 409 upgrade_request_not_pending (including the current status) if the request already completed, was declined, canceled, or expired.
await client.billing.upgradeRequests.cancel('upr_abc123');client.billing.upgrade_requests.cancel("upr_abc123")chronary billing upgrade-request cancel upr_abc123Upgrade request object
Section titled “Upgrade request object”| Field | Description |
|---|---|
id | Upgrade request ID (upr_ prefix) |
status | pending, completed, declined, canceled, or expired |
plan | The plan requested (pro) |
interval | month or year |
reason | The free text you supplied, or null. Always render as escaped plain text |
feature / metric | What was blocked, or null |
requested_via | api or mcp |
requested_by_key_prefix | First 20 characters of the key that made the request — never the full key |
approval_url | Link to give your human; null once the request is no longer pending |
created_at / expires_at | ISO-8601 timestamps |
completed_at / declined_at / canceled_at | ISO-8601 timestamps, or null |
owner_notification | Create responses only: queued, not_configured, skipped_internal, or already_notified |
reused | Create responses only: true when an already-pending request was returned |
MCP tools
Section titled “MCP tools”The same operations are available as MCP tools for org-level keys: get_billing, request_upgrade, get_upgrade_request, and cancel_upgrade_request. See the MCP Tools Reference.