Skip to content

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.

  1. Don’t retry in a loop. A 403 plan_required or a 429 quota_exceeded will not clear by retrying.
  2. Call GET /v1/billing to see the current plan and any request already pending.
  3. Call POST /v1/billing/upgrade-requests to create an upgrade request. Pass the feature or metric from the error so your human sees what was blocked.
  4. Give the returned approval_url to your human.
  5. Poll GET /v1/billing/upgrade-requests/{id} at most once a minute until status is no longer pending.
  6. After status: completed, retry the original call about 15 seconds later.

See Error Codes for the error bodies that carry upgrade_url.

GET /v1/billing

Returns a read-only billing summary for the organization, including any pending upgrade request.

Terminal window
curl https://api.chronary.ai/v1/billing \
-H "Authorization: Bearer chr_sk_your_key_here"
{
"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
}

All amounts are in minor units (2900 = $29.00) and exclude tax.

FieldDescription
planCurrent plan, for example free, pro, or scale
plan_sourceHow the plan was granted, or null
plan_expires_atISO-8601 end of a time-limited plan grant, or null
statusSubscription status; null if the account never subscribed
intervalmonth or year, or null
current_period_endISO-8601 end of the current paid period, or null
cancel_atISO-8601 time a scheduled cancellation takes effect, or null
cancel_at_period_endtrue when a cancellation is scheduled
renews_atISO-8601 next renewal, or null when no renewal is expected
price{ amount, currency, interval, tax_exclusive } for the active subscription, or null
has_billing_accounttrue once the account has a billing profile (invoices and payment methods live in the console)
self_serve_upgradetrue when a human can start a secure checkout right now
upgrade_optionsPlans a human can upgrade to; empty when self_serve_upgrade is false
upgrade_urlConsole link for a human to upgrade, or null
manage_urlConsole billing page
contact_urlContact link for plans that need a conversation (Scale), or null
pending_upgrade_requestThe pending upgrade request, or null
offerThe founding member offer for early-access accounts, or null

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
}
FieldDescription
kindAlways founding
statusupcoming — 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_atavailable: when free Pro and the offer end. Otherwise null
trial_untilavailable: 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_percentDiscount on the founding terms, or null when none applies
discount_monthsHow many paid months the discount covers (annual plans: the first year), or null
renewal_consentredeemed 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.

POST /v1/billing/upgrade-requests

Asks 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.

FieldTypeRequiredDescription
intervalstringNomonth (default) or year
reasonstring (max 500)NoWhy the upgrade is needed. Shown to your human as plain text
featurestringNoThe plan-gated feature that was blocked, for example scoped_keys
metricstringNoThe quota metric that was exhausted, for example api_calls
Terminal window
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"}'

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
}
StatustypeMeaning
400validation_errorInvalid body (for example interval not month or year, or reason over 500 characters)
403forbiddenAgent-scoped key; use an org-level key
403use_billing_pageCalled from a console session; use the billing page instead
403email_verification_requiredThe account’s email isn’t verified yet
409already_subscribedThe account already has an active subscription
409unsupported_planThe current plan can’t self-serve upgrade; the error includes contact_url
409founding_offer_pendingEarly-access account before launch — Pro is still free, and the founding member offer opens at launch. Don’t ask your human to pay yet
409upgrade_request_recently_declinedA human recently declined; wait retry_after seconds before asking again
429upgrade_request_limitToo many requests; wait retry_after seconds
503billing_not_configuredBilling isn’t available in this environment
GET /v1/billing/upgrade-requests/{id}

Poll at most once a minute. When status leaves pending, stop polling.

Terminal window
curl https://api.chronary.ai/v1/billing/upgrade-requests/upr_abc123 \
-H "Authorization: Bearer chr_sk_your_key_here"

Returns the upgrade request object, or 404 not_found.

POST /v1/billing/upgrade-requests/{id}/cancel

Withdraws 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');
FieldDescription
idUpgrade request ID (upr_ prefix)
statuspending, completed, declined, canceled, or expired
planThe plan requested (pro)
intervalmonth or year
reasonThe free text you supplied, or null. Always render as escaped plain text
feature / metricWhat was blocked, or null
requested_viaapi or mcp
requested_by_key_prefixFirst 20 characters of the key that made the request — never the full key
approval_urlLink to give your human; null once the request is no longer pending
created_at / expires_atISO-8601 timestamps
completed_at / declined_at / canceled_atISO-8601 timestamps, or null
owner_notificationCreate responses only: queued, not_configured, skipped_internal, or already_notified
reusedCreate responses only: true when an already-pending request was returned

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.