Account, Billing, Webhooks, OAuth & Brands API
Platform management surfaces. Account management is free — no credits are charged for reading or configuring your account, keys, webhooks, or OAuth apps. The only money surfaces are Billing (Stripe checkout / subscriptions / add-ons) and creating an additional Brand (first brand free; extra sub-brands cost credits). Covered:
- Account (
/v1/account) — account info, credit balance, dashboard overview, billing snapshot, add-on entitlements, sandbox snapshot, auto-recharge, subscription cancel/resume, add-on cancel, rank-tracking keywords, audit-export jobs. - API Keys (
/v1/account/keys) — create, list, get one, edit, revoke. - Usage (
/v1/account/usage) — usage time series, activity log, audit-log CSV export. - Billing (
/v1/billing) — Stripe checkout (packages + pay-as-you-go), invoices, subscription, add-ons. - Webhooks (
/v1/webhooks) — endpoint CRUD, replay, dead-letter queue. - OAuth Apps (
/v1/oauth/apps) — register, list, and revoke your own OAuth clients. - Brands (
/v1/brands) — brand onboarding (first brand free; extra sub-brands cost credits).
Authentication
Authorization: Bearer ai92_live_<key> # production
Authorization: Bearer ai92_test_<key> # sandboxOAuth 2.0 access tokens are also accepted. Mutating requests support Idempotency-Key.
Account
GET /v1/account
Cost: Free
Account info and balance.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"id": "acct_88",
"tier": "growth",
"credit_balance": 1497,
"credit_balance_durable": 1500,
"is_active": true,
"read_only_mode": false,
"subscription_tier": "growth",
"mass_mail_addon_active": false,
"rank_tracking_addon_tier": null,
"last_request_at": "2026-06-21T08:55:00Z",
"created_at": "2026-05-01T12:00:00Z"
}GET /v1/account/credits
Cost: Free
Current credit balance.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/credits \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{ "balance": 1497, "tier": "growth" }GET /v1/account/overview
Cost: Free
Dashboard overview: account, usage, subscription, and add-ons in a single call.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/overview \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"account_id": "acct_88",
"tier": "growth",
"credit_balance": 1497,
"rate_limit_rps": 25,
"is_active": true,
"read_only_mode": false,
"read_only_since": null,
"first_request_at": "2026-05-01T12:05:00Z",
"last_request_at": "2026-06-21T08:55:00Z",
"usage_today_credits": 120,
"usage_this_month_credits": 4310,
"recent_status_counts": { "2xx": 980, "4xx": 12, "5xx": 0 },
"webhook_delivery_rate_7d": null,
"subscription": { "tier": "growth", "status": null, "current_period_end": null, "bundled_credits_per_period": null },
"addons": { "mass_mail_active": false, "rank_tracking_tier": null }
}GET /v1/account/billing
Cost: Free
One-shot billing read powering the dashboard billing page: current balance + month-to-date usage, the credit-package catalog, your auto-recharge config, and the most recent ledger entries. Invoices are served separately by GET /v1/billing/invoices; this snapshot returns an empty recent_invoices list so the page renders without a cross-module round-trip.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/billing \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"account_id": "acct_88",
"credit_balance": 1497,
"usage_this_month_credits": 4310,
"packages": [
{ "code": "STARTER", "name": "Starter", "credits": 1000, "price_usd_cents": 2900, "per_credit_usd": 0.029, "savings_pct": 0, "rate_limit_rps": 10 }
],
"auto_recharge": {
"enabled": false,
"min_balance_threshold": 500,
"recharge_package_code": "starter",
"payment_method_last4": null,
"payment_method_brand": null,
"last_triggered_at": null,
"last_triggered_success": null
},
"recent_invoices": [],
"recent_transactions": [
{ "id": "txn_9a", "type": "debit", "amount": -3, "balance_after": 1497, "service": "contacts", "endpoint": "/v1/contacts/reveal", "created_at": "2026-06-21T08:55:00Z", "reason": null }
]
}GET /v1/account/addons
Cost: Free
Add-on entitlements snapshot for the two subscription add-ons: Mass Mail and Rank Tracking (quota + usage + current period end).
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/addons \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"mass_mail": {
"active": false,
"recipients_quota": 0,
"recipients_used_mtd": 0,
"current_period_end": null
},
"rank_tracking": {
"tier": null,
"keyword_quota": 0,
"keyword_count": 0,
"current_period_end": null
}
}GET /v1/account/sandbox
Cost: Free
Sandbox key + usage snapshot: the most recent sandbox key's prefix/last4 and your sandbox request volume (today, this month, and success rate).
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/sandbox \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"key_prefix": "ai92_test",
"key_last4": "9f2a",
"created_at": "2026-05-01T12:00:00Z",
"requests_today": 14,
"requests_this_month": 318,
"success_rate": 0.9874
}Auto-Recharge
PATCH /v1/account/auto-recharge
Cost: Free
Update the account's auto-recharge config (top up automatically when the balance drops below a threshold). Any omitted field is left unchanged.
Auth: Bearer
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean / null | No | Turn auto-recharge on or off. |
min_balance_threshold | integer / null | No | Trigger when balance falls below this (>= 0). |
recharge_package_code | string / null | No | Package to buy on trigger (e.g. starter). Case-insensitive. |
Request
curl -X PATCH https://api.ai92.ai/v1/account/auto-recharge \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"enabled": true, "min_balance_threshold": 1000, "recharge_package_code": "growth"}'Response 200 OK
{
"enabled": true,
"min_balance_threshold": 1000,
"recharge_package_code": "growth",
"payment_method_last4": null,
"payment_method_brand": null,
"last_triggered_at": null,
"last_triggered_success": null
}Errors
| Status | Code | When |
|---|---|---|
400 | invalid recharge_package_code | recharge_package_code is not a known package. |
Subscription Management
These manage an existing hybrid subscription / add-on through module-a's Stripe integration. They are free to call; the durable state flips when Stripe's webhook propagates. To start a subscription or add-on, use the Billing endpoints below.
POST /v1/account/subscription/cancel
Cost: Free
Cancel the active hybrid subscription at the end of the current period.
Auth: Bearer
Request
curl -X POST https://api.ai92.ai/v1/account/subscription/cancel \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"tier": "growth",
"status": "active",
"current_period_end": "2026-07-01T00:00:00Z",
"bundled_credits_per_period": 5000,
"cancel_at_period_end": true
}Errors
| Status | Code | When |
|---|---|---|
404 | (no active subscription) | The account has no active subscription. |
POST /v1/account/subscription/resume
Cost: Free
Resume a subscription that is scheduled to cancel at period end.
Auth: Bearer
Request
curl -X POST https://api.ai92.ai/v1/account/subscription/resume \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"tier": "growth",
"status": "active",
"current_period_end": "2026-07-01T00:00:00Z",
"bundled_credits_per_period": 5000,
"cancel_at_period_end": false
}Errors
| Status | Code | When |
|---|---|---|
404 | (no active subscription) | The account has no active subscription. |
POST /v1/account/addons/mass-mail/cancel
Cost: Free
Cancel the Mass Mail add-on at the end of the current period.
Auth: Bearer
Request
curl -X POST https://api.ai92.ai/v1/account/addons/mass-mail/cancel \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"active": true,
"recipients_quota": 50000,
"recipients_used_mtd": 0,
"current_period_end": "2026-07-01T00:00:00Z"
}Errors
| Status | Code | When |
|---|---|---|
404 | (no active Mass Mail add-on) | The account has no active Mass Mail add-on. |
Rank Tracking Keywords
Manage the keywords tracked by the Rank Tracking add-on. Requires the add-on to be active (otherwise POST returns 403 NO_RANK_TRACKING_ADDON).
GET /v1/account/addons/rank-tracking/keywords
Cost: Free
List tracked keywords plus the current tier and quota.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/addons/rank-tracking/keywords \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"tier": "pro",
"quota": 100,
"keywords": [
{ "id": "kw_01", "term": "crm software", "last_position": 7, "last_checked_at": "2026-06-21T06:00:00Z" }
]
}POST /v1/account/addons/rank-tracking/keywords
Cost: Free
Add a tracked keyword. Returns 201 Created.
Auth: Bearer · Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
term | string | Yes | Keyword to track. Length 1–255. |
domain | string / null | No | Domain to track the keyword for. Max length 255. |
Request
curl https://api.ai92.ai/v1/account/addons/rank-tracking/keywords \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"term": "crm software", "domain": "acme.com"}'Response 201 Created
{ "id": "kw_01", "term": "crm software", "last_position": null, "last_checked_at": null }Errors
| Status | Code | When |
|---|---|---|
403 | NO_RANK_TRACKING_ADDON | The Rank Tracking add-on is not active. |
403 | RANK_TRACKING_QUOTA_REACHED | The keyword quota for your tier is full. |
DELETE /v1/account/addons/rank-tracking/keywords/{keyword_id}
Cost: Free
Remove a tracked keyword.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
keyword_id | path | string | Yes | The keyword id. |
Request
curl -X DELETE https://api.ai92.ai/v1/account/addons/rank-tracking/keywords/kw_01 \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{ "deleted": true, "id": "kw_01" }Errors
| Status | Code | When |
|---|---|---|
404 | (keyword not found) | Keyword does not exist or is not owned by the account. |
Audit Log Exports
List and re-run audit-log CSV export jobs. To trigger a new export, use POST /v1/account/usage/export (see Usage); poll its status with GET /v1/account/usage/export/{job_id}.
GET /v1/account/audit/export
Cost: Free
List the account's audit-log export jobs (most recent first, up to 50).
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/audit/export \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
[
{
"id": "exp_01",
"filters": { "from": "2026-06-01", "to": "2026-06-21" },
"status": "completed",
"requested_at": "2026-06-21T09:00:00Z",
"completed_at": "2026-06-21T09:00:20Z",
"result_url": "https://cdn.ai92.ai/exports/exp_01.csv",
"result_expires_at": "2026-06-28T09:00:20Z",
"row_count": 1284,
"failure_reason": null
}
]POST /v1/account/audit/export/{job_id}/re-export
Cost: Free
Re-run a prior audit export with the same filters. Returns a new queued job.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
job_id | path | string | Yes | The id of a prior export job to re-run. |
Request
curl -X POST https://api.ai92.ai/v1/account/audit/export/exp_01/re-export \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"id": "exp_02",
"filters": { "from": "2026-06-01", "to": "2026-06-21" },
"status": "queued",
"requested_at": "2026-06-21T10:00:00Z",
"completed_at": null,
"result_url": null,
"result_expires_at": null,
"row_count": null,
"failure_reason": null
}Errors
| Status | Code | When |
|---|---|---|
404 | (export job not found) | Job does not exist or is not owned by the account. |
API Keys
POST /v1/account/keys
Cost: Free
Generate a new API key. The plaintext key is shown once in the response.
Auth: Bearer · Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Key name. Length 1–128. |
kind | string | No | production (default) or sandbox. |
expires_in_days | integer / null | No | Expiry. Range 1–730. |
ip_whitelist | string[] | No | Allowed CIDRs. Default []. |
daily_credit_limit | integer / null | No | Per-key daily limit (>= 1). |
monthly_credit_limit | integer / null | No | Per-key monthly limit (>= 1). |
Request
curl https://api.ai92.ai/v1/account/keys \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"name": "CI key", "kind": "production", "expires_in_days": 90, "daily_credit_limit": 500}'Response 201 Created
{
"id": "key_01",
"name": "CI key",
"kind": "production",
"prefix": "ai92_live",
"last4": "9f2a",
"plaintext_key": "ai92_live_9c0de…9f2a",
"created_at": "2026-06-21T09:00:00Z",
"expires_at": "2026-09-19T09:00:00Z",
"ip_whitelist": [],
"daily_credit_limit": 500,
"monthly_credit_limit": null,
"warning": "Store this key now. It will not be shown again."
}GET /v1/account/keys
Cost: Free
List API keys (metadata only — never the plaintext key).
Auth: Bearer
Request
curl https://api.ai92.ai/v1/account/keys \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"keys": [
{
"id": "key_01", "name": "CI key", "kind": "production",
"prefix": "ai92_live", "last4": "9f2a",
"created_at": "2026-06-21T09:00:00Z", "expires_at": "2026-09-19T09:00:00Z",
"last_used_at": "2026-06-21T09:10:00Z", "revoked_at": null,
"ip_whitelist": [], "daily_credit_limit": 500, "monthly_credit_limit": null
}
]
}GET /v1/account/keys/{key_id}
Cost: Free
Get one API key's metadata (never the plaintext key).
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
key_id | path | string | Yes | The key id. |
Request
curl https://api.ai92.ai/v1/account/keys/key_01 \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"id": "key_01",
"name": "CI key",
"kind": "production",
"prefix": "ai92_live",
"last4": "9f2a",
"created_at": "2026-06-21T09:00:00Z",
"expires_at": "2026-09-19T09:00:00Z",
"last_used_at": "2026-06-21T09:10:00Z",
"revoked_at": null,
"ip_whitelist": [],
"daily_credit_limit": 500,
"monthly_credit_limit": null
}Errors
| Status | Code | When |
|---|---|---|
404 | (key not found) | Key does not exist or is not owned by the account. |
PATCH /v1/account/keys/{key_id}
Cost: Free
Edit a key's name, allowlist, or spending limits.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
key_id | path | string | Yes | The key id. |
name | body | string / null | No | New name. Max length 128. |
ip_whitelist | body | string[] / null | No | New allowlist. |
daily_credit_limit | body | integer / null | No | New daily limit (>= 0). |
monthly_credit_limit | body | integer / null | No | New monthly limit (>= 0). |
Request
curl -X PATCH https://api.ai92.ai/v1/account/keys/key_01 \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"daily_credit_limit": 1000}'Response 200 OK
{ "ok": true }Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Key does not exist or is not owned by the account. |
DELETE /v1/account/keys/{key_id}
Cost: Free
Revoke an API key.
Auth: Bearer · Returns 204 No Content
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
key_id | path | string | Yes | The key id. |
Request
curl -X DELETE https://api.ai92.ai/v1/account/keys/key_01 \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 204 No Content (empty body)
Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Key does not exist or is not owned by the account. |
Usage
GET /v1/account/usage
Cost: Free
Usage history: daily aggregate credits plus a per-service breakdown.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
range_days | query | integer | No | Look-back window. Default 30. |
service | query | string / null | No | Filter by service. |
Request
curl "https://api.ai92.ai/v1/account/usage?range_days=7" \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"range_days": 7,
"service_filter": null,
"daily_series": [ { "date": "2026-06-20", "credits": 120 } ],
"service_breakdown": [ { "service": "contacts", "credits": 540, "count": 88 } ]
}GET /v1/account/usage/log
Cost: Free
Paginated activity log.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
cursor | query | string / null | No | Pagination cursor. |
limit | query | integer | No | Page size. Default 50. |
service | query | string / null | No | Filter by service. |
status_class | query | string / null | No | 2xx, 4xx, or 5xx. |
Request
curl "https://api.ai92.ai/v1/account/usage/log?limit=2&status_class=2xx" \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"items": [
{
"request_id": "req_9a", "ts": "2026-06-21T08:55:00Z", "method": "POST",
"endpoint": "/v1/contacts/reveal", "service": "contacts", "status": 200,
"credits": 3, "latency_ms": 142, "trace_id": "tr_77", "backend_module": "module-H"
}
],
"next_cursor": "eyJvZmZzZXQiOjJ9"
}POST /v1/account/usage/export
Cost: Free
Trigger an audit-log CSV export (async). Poll the returned job.
Auth: Bearer
| Field | Type | Required | Description |
|---|---|---|---|
since | datetime | Yes | Export lower bound (ISO 8601). |
until | datetime / null | No | Export upper bound. |
include_credit_transactions | boolean | No | Include credit transactions. Default true. |
include_request_log | boolean | No | Include request log. Default true. |
Request
curl https://api.ai92.ai/v1/account/usage/export \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"since": "2026-06-01T00:00:00Z", "include_credit_transactions": true, "include_request_log": true}'Response 200 OK
{
"job_id": "5f2c0a91-…",
"status": "queued",
"poll_url": "/v1/account/usage/export/5f2c0a91-…"
}GET /v1/account/usage/export/{job_id}
Cost: Free
Poll an audit-export job.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
job_id | path | string | Yes | The export job id. |
Request
curl https://api.ai92.ai/v1/account/usage/export/5f2c0a91-… \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"job_id": "5f2c0a91-…",
"status": "completed",
"result_url": "https://cdn.ai92.ai/exports/5f2c0a91.csv",
"result_expires_at": "2026-06-22T09:00:00Z",
"row_count": 4210,
"error_message": null
}Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Job does not exist or is not owned by the account. |
Billing
POST /v1/billing/checkout
Cost: Free
Start a credit-package purchase via Stripe (proxied to module-a).
Auth: Bearer
| Field | Type | Required | Description |
|---|---|---|---|
package_code | string (enum) | Yes | Credit package code. |
success_url | URL | Yes | Redirect on success. |
cancel_url | URL | Yes | Redirect on cancel. |
Request
curl https://api.ai92.ai/v1/billing/checkout \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"package_code": "GROWTH", "success_url": "https://app.ai92.ai/ok", "cancel_url": "https://app.ai92.ai/cancel"}'Response 200 OK
{ "session_id": "cs_test_…", "url": "https://checkout.stripe.com/c/pay/cs_test_…" }POST /v1/billing/checkout-custom
Cost: Free
Pay-as-you-go: start a Stripe Checkout for an arbitrary credit amount. module-a prices it at the floor pack tier and builds a Stripe price_data line item; on payment its webhook grants the credits. In the sandbox this returns a mock checkout URL and never charges.
Auth: Bearer
| Field | Type | Required | Description |
|---|---|---|---|
credits | integer | Yes | Credits to buy. Range 1000–1000000. |
success_url | URL | Yes | Redirect after a successful payment. |
cancel_url | URL | Yes | Redirect if the customer cancels. |
Request
curl https://api.ai92.ai/v1/billing/checkout-custom \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"credits": 25000, "success_url": "https://app.acme.com/ok", "cancel_url": "https://app.acme.com/cancel"}'Response 200 OK
{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_…" }In the sandbox the response is { "checkout_url": "https://checkout.stripe.com/sandbox/mock", "sandbox": true }.
GET /v1/billing/invoices
Cost: Free
List invoices (proxied to module-a).
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
page | query | integer | No | 1-based page number. Default 1. |
page_size | query | integer | No | Items per page. Default 50. |
Unlike the cursor-paginated list endpoints,
GET /v1/billing/invoicesuses offset pagination: passpage/page_sizeand readtotal/page/page_sizeback.
Request
curl "https://api.ai92.ai/v1/billing/invoices?page=1&page_size=10" \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"total": 1,
"page": 1,
"page_size": 10,
"items": [ { "id": "in_01", "amount": 9900, "currency": "usd", "created_at": "2026-06-01T00:00:00Z" } ]
}GET /v1/billing/invoices/{invoice_id}/pdf
Cost: Free
Download an invoice PDF (proxied to module-a).
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
invoice_id | path | string | Yes | The invoice id. |
Request
curl https://api.ai92.ai/v1/billing/invoices/in_01/pdf \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{ "download_url": "https://cdn.ai92.ai/invoices/in_01.pdf" }POST /v1/billing/subscription
Cost: Free
Start a Stripe Checkout for a hybrid subscription tier. On payment, the billing webhook activates the subscription and grants the bundled credits automatically.
Auth: Bearer
| Field | Type | Required | Description |
|---|---|---|---|
tier | string (enum) | Yes | pro_sub, business_sub, or enterprise_sub. |
success_url | URL | Yes | Redirect on success. |
cancel_url | URL | Yes | Redirect on cancel. |
Request
curl -X POST https://api.ai92.ai/v1/billing/subscription \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"tier": "pro_sub", "success_url": "https://app.acme.com/ok", "cancel_url": "https://app.acme.com/cancel"}'Response 200 OK
{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_…" }In the sandbox the response is { "checkout_url": "https://checkout.stripe.com/sandbox/mock", "sandbox": true } and nothing is charged.
GET /v1/billing/subscription
Cost: Free
Current subscription plus the hybrid tier catalog (the same snapshot the dashboard subscription panel renders).
Auth: Bearer
Request
curl https://api.ai92.ai/v1/billing/subscription \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"current": {
"tier": "pro_sub",
"status": "active",
"current_period_end": "2026-08-01T00:00:00Z",
"bundled_credits_per_period": 4000,
"cancel_at_period_end": false
},
"available_tiers": [
{ "code": "pro_sub", "price_usd_cents": 7900, "bundled_credits_per_period": 4000, "bonus_pct": 20 },
{ "code": "business_sub", "price_usd_cents": 27900, "bundled_credits_per_period": 18000, "bonus_pct": 20 },
{ "code": "enterprise_sub", "price_usd_cents": 149900, "bundled_credits_per_period": 130000, "bonus_pct": 20 }
]
}current is null-ish (tier: null) when the account has no subscription. Each period the grant is the bundle plus the renewal bonus (bonus_pct) — e.g. 4,000 + 20% = 4,800 credits on Pro.
POST /v1/billing/addons/{kind}
Cost: Free
Start a Stripe Checkout for a monthly add-on. On payment, the billing webhook activates the add-on entitlement automatically.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
kind | path | string (enum) | Yes | Add-on kind: mass_mail or rank_tracking. |
success_url | body | URL | Yes | Redirect on success. |
cancel_url | body | URL | Yes | Redirect on cancel. |
Request
curl -X POST https://api.ai92.ai/v1/billing/addons/mass_mail \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"success_url": "https://app.acme.com/ok", "cancel_url": "https://app.acme.com/cancel"}'Response 200 OK
{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_…" }In the sandbox the response is { "checkout_url": "https://checkout.stripe.com/sandbox/mock", "sandbox": true } and nothing is charged.
Webhooks
Allowed event types: contact.verified, campaign.sent, campaign.reply, campaign.open, campaign.click, campaign.bounce, visitor.identified, review.new, credits.low, seo.alert, key.expiring, account.inactivity_warning, account.read_only, account.reactivated, sla.violation.
Subscribing to any name outside this list returns 422 UNKNOWN_EVENT_TYPE and the endpoint is not created. There is no job.completed/scan.completed event — async job and scan results are polled, not delivered by webhook (see Async Jobs).
POST /v1/webhooks
Cost: Free
Create a webhook endpoint. The signing secret is shown once.
Auth: Bearer · Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
url | URL | Yes | Destination URL. |
description | string / null | No | Description. Max length 255. |
events | string[] | No | Subscribed events (must be from the allowed list; at least one required). Default []. |
Request
curl https://api.ai92.ai/v1/webhooks \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.acme.com/ai92", "description": "Prod hook", "events": ["campaign.sent", "credits.low"]}'Response 201 Created
{
"id": "wh_01",
"url": "https://hooks.acme.com/ai92",
"description": "Prod hook",
"events": ["campaign.sent", "credits.low"],
"active": true,
"created_at": "2026-06-21T09:00:00Z",
"secret": "whsec_…",
"warning": "Store this signing secret now. It will not be shown again."
}Errors
| Status | Code | When |
|---|---|---|
422 | VALIDATION_ERROR | Unknown event types, or no events provided. |
503 | WEBHOOKS_UNAVAILABLE | Webhook signing not configured on this environment. |
GET /v1/webhooks
Cost: Free
List webhook endpoints.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/webhooks \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"endpoints": [
{
"id": "wh_01", "url": "https://hooks.acme.com/ai92", "description": "Prod hook",
"events": ["campaign.sent", "credits.low"], "active": true,
"last_success_at": "2026-06-21T08:00:00Z", "last_failure_at": null,
"created_at": "2026-06-21T09:00:00Z"
}
]
}GET /v1/webhooks/{endpoint_id}
Cost: Free
Get one webhook endpoint (metadata + last success/failure timestamps; never the signing secret, which is shown once at creation).
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
endpoint_id | path | string | Yes | The webhook endpoint id. |
Request
curl https://api.ai92.ai/v1/webhooks/wh_01 \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"id": "wh_01",
"url": "https://hooks.acme.com/ai92",
"description": "Prod hook",
"events": ["campaign.sent", "credits.low"],
"active": true,
"last_success_at": "2026-06-21T09:10:00Z",
"last_failure_at": null,
"created_at": "2026-06-21T09:00:00Z"
}Errors
| Status | Code | When |
|---|---|---|
404 | (endpoint not found) | Endpoint does not exist or is not owned by the account. |
DELETE /v1/webhooks/{endpoint_id}
Cost: Free
Delete a webhook endpoint.
Auth: Bearer · Returns 204 No Content
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
endpoint_id | path | string | Yes | The endpoint id. |
Request
curl -X DELETE https://api.ai92.ai/v1/webhooks/wh_01 \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 204 No Content (empty body)
Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Endpoint does not exist or is not owned by the account. |
POST /v1/webhooks/{endpoint_id}/replay
Cost: Free
Replay events to an endpoint over a date range. Generates fresh deliveries.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
endpoint_id | path | string | Yes | The endpoint id. |
since | body | datetime | Yes | Replay lower bound. |
until | body | datetime / null | No | Replay upper bound. |
event_types | body | string[] | No | Restrict to specific event types. Default []. |
Request
curl https://api.ai92.ai/v1/webhooks/wh_01/replay \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"since": "2026-06-20T00:00:00Z", "event_types": ["campaign.sent"]}'Response 200 OK
{ "replay_count": 42, "status": "queued" }GET /v1/webhooks/{endpoint_id}/dlq
Cost: Free
List dead-letter deliveries for an endpoint.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
endpoint_id | path | string | Yes | The endpoint id. |
limit | query | integer | No | Page size. Default 50. |
cursor | query | string / null | No | Pagination cursor. |
Request
curl "https://api.ai92.ai/v1/webhooks/wh_01/dlq?limit=20" \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"deliveries": [
{
"id": "dlq_5", "event_id": "ev_9", "event_type": "campaign.sent",
"attempts": 6, "last_status_code": 500, "last_error": "upstream timeout",
"last_attempt_at": "2026-06-21T07:00:00Z"
}
],
"next_cursor": null
}POST /v1/webhooks/{endpoint_id}/dlq/redrive
Cost: Free
Re-dispatch dead-letter deliveries. Unlike /replay, this re-tries the same delivery rows, so handlers see the same X-AI92-Delivery-Id and can dedupe naturally.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
endpoint_id | path | string | Yes | The endpoint id. |
delivery_ids | body | string[] | No | Specific delivery ids. Default []. Max 500. |
max_count | body | integer | No | Max deliveries to redrive. Default 100. Range 1–500. |
Request
curl https://api.ai92.ai/v1/webhooks/wh_01/dlq/redrive \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"delivery_ids": ["dlq_5"], "max_count": 100}'Response 200 OK
{ "endpoint_id": "wh_01", "redriven_count": 1, "status": "queued" }Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Endpoint does not exist or is not owned by the account. |
OAuth Apps
Register and manage your own OAuth 2.0 clients (the same apps you see in the OAuth panel). These let you issue OAuth access tokens for your account instead of static API keys. This is the customer-facing surface under /v1/oauth/apps; it is distinct from the operator-only admin OAuth router.
Available scopes: contacts:read, contacts:write, campaigns:read, campaigns:create, campaigns:send, seo:read, markets:read, reviews:read, reviews:reply, cloud:read, cloud:write, webhooks:manage, account:read, account:write, offline_access.
GET /v1/oauth/apps
Cost: Free
List the account's OAuth apps with per-app token counts and last-used timestamp.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/oauth/apps \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
[
{
"client_id": "cli_3f9a…",
"name": "Acme Integration",
"description": "Server-to-server sync",
"redirect_uris": ["https://app.acme.com/oauth/callback"],
"scopes": ["contacts:read", "campaigns:read", "offline_access"],
"created_at": "2026-06-10T12:00:00Z",
"suspended_at": null,
"tokens_issued_count": 4,
"active_tokens_count": 2,
"last_used_at": "2026-06-21T08:00:00Z"
}
]POST /v1/oauth/apps
Cost: Free
Register a new OAuth app. The client_secret is returned once in the response — store it securely.
Auth: Bearer · Returns 201 Created
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | App name. Length 1–128. |
description | string / null | No | Description. Max length 1024. |
redirect_uris | string[] | No | Allowed OAuth redirect URIs. Default []. |
scopes | string[] | No | Requested scopes (must be from the allowed list). Default []. |
Request
curl https://api.ai92.ai/v1/oauth/apps \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Integration", "redirect_uris": ["https://app.acme.com/oauth/callback"], "scopes": ["contacts:read", "offline_access"]}'Response 201 Created
{
"app": {
"client_id": "cli_3f9a…",
"name": "Acme Integration",
"description": null,
"redirect_uris": ["https://app.acme.com/oauth/callback"],
"scopes": ["contacts:read", "offline_access"],
"created_at": "2026-06-21T09:00:00Z",
"suspended_at": null,
"tokens_issued_count": 0,
"active_tokens_count": 0,
"last_used_at": null
},
"client_secret": "s3cr3t_…"
}Errors
| Status | Code | When |
|---|---|---|
400 | INVALID_SCOPE | One or more requested scopes are not in the allowed list. |
DELETE /v1/oauth/apps/{client_id}
Cost: Free
Revoke (suspend) an OAuth app. The app is suspended and all of its outstanding tokens are revoked.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
client_id | path | string | Yes | The OAuth client id to revoke. |
Request
curl -X DELETE https://api.ai92.ai/v1/oauth/apps/cli_3f9a… \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{ "client_id": "cli_3f9a…", "revoked": true }Errors
| Status | Code | When |
|---|---|---|
404 | (app not found) | App does not exist or is not owned by the account. |
Brands
Brand onboarding turns an account into one or more operational brands across the fleet. The first brand is free; each additional sub-brand costs brand_create_cost credits (charged post-success). Sandbox onboarding is always free.
POST /v1/brands
Cost: Free for the first brand; configured credit cost for each additional sub-brand (sandbox always free)
Register (or link) a brand. Creates the brand in module-a (the fleet source of truth) and persists the 923 brand row.
Auth: Bearer
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Brand name. Length 1–200. |
domain | string / null | No | Brand domain. Max length 255. Enables SEO/AI-visibility/markets readiness. |
logo_url | string / null | No | Logo URL. Max length 1024. |
brand_colors | object / null | No | Brand color palette. |
tone | string / null | No | Brand tone. Max length 64. |
link_existing_brand_id | string / null | No | Link an existing module-a brand instead of creating one. Max length 128. |
Request
curl https://api.ai92.ai/v1/brands \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "domain": "acme.com", "tone": "confident"}'Response 200 OK
{
"brand_id": "b1f0…",
"canonical_brand_id": "acme",
"name": "Acme Corp",
"domain": "acme.com",
"status": "active",
"readiness": {
"creative": true, "build_campaign": true, "seo": true, "ai_visibility": true,
"markets": true, "intent": false, "contacts": false, "reviews": false
},
"created_at": "2026-06-21T09:00:00Z",
"credits_charged": 0
}Errors
| Status | Code | When |
|---|---|---|
402 | INSUFFICIENT_CREDITS | Sub-brand created upstream but balance too low to finalize billing. |
GET /v1/brands
Cost: Free
List the account's brands.
Auth: Bearer
Request
curl https://api.ai92.ai/v1/brands \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"brands": [
{ "brand_id": "b1f0…", "canonical_brand_id": "acme", "name": "Acme Corp", "domain": "acme.com", "status": "active", "readiness": {}, "created_at": "2026-06-21T09:00:00Z" }
],
"count": 1
}GET /v1/brands/{brand_id}
Cost: Free
Get a single brand.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
Request
curl https://api.ai92.ai/v1/brands/b1f0… \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{ "brand_id": "b1f0…", "canonical_brand_id": "acme", "name": "Acme Corp", "domain": "acme.com", "status": "active", "readiness": {}, "created_at": "2026-06-21T09:00:00Z" }Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand does not exist or is not owned by the account. |
GET /v1/brands/{brand_id}/readiness
Cost: Free
Per-product readiness and what is still missing to unlock each product.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
Request
curl https://api.ai92.ai/v1/brands/b1f0…/readiness \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"brand_id": "b1f0…",
"readiness": { "creative": true, "seo": true, "intent": false, "contacts": false, "reviews": false },
"next_steps": {
"intent": "POST /v1/brands/{id}/tracking and install the pixel",
"contacts": "POST /v1/brands/{id}/icp",
"reviews": "POST /v1/brands/{id}/connections/gbp"
}
}Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand not owned by the account. |
POST /v1/brands/{brand_id}/icp
Cost: Free
Set the brand's ideal-customer profile — enables Contact Finder.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
industries | body | string[] / null | No | Target industries. |
titles | body | string[] / null | No | Target job titles. |
geos | body | string[] / null | No | Target geographies. |
company_sizes | body | string[] / null | No | Target company sizes. |
Request
curl https://api.ai92.ai/v1/brands/b1f0…/icp \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"industries": ["software"], "titles": ["VP Marketing"], "geos": ["US"]}'Response 200 OK
{ "brand_id": "b1f0…", "readiness": { "contacts": true } }Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand not owned by the account. |
POST /v1/brands/{brand_id}/seo
Cost: Free
Set SEO context (domain, keywords, competitors) — enables SEO, AI-Visibility, and Markets.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
domain | body | string | Yes | Brand domain. Length 1–255. |
keywords | body | string[] / null | No | Seed keywords. |
competitors | body | string[] / null | No | Competitor domains. |
Request
curl https://api.ai92.ai/v1/brands/b1f0…/seo \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"domain": "acme.com", "keywords": ["crm"], "competitors": ["rival.com"]}'Response 200 OK
{ "brand_id": "b1f0…", "readiness": { "seo": true, "ai_visibility": true, "markets": true } }Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand not owned by the account. |
POST /v1/brands/{brand_id}/tracking
Cost: Free
Get the tracking pixel — enables Who-Is-Searching once traffic is detected.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
Request
curl -X POST https://api.ai92.ai/v1/brands/b1f0…/tracking \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"brand_id": "b1f0…",
"pixel_id": "ai92px_b1f0a2c3d4e5f607",
"snippet": "<script async src=\"https://px.ai92.ai/p.js\" data-ai92-pixel=\"ai92px_b1f0a2c3d4e5f607\"></script>",
"note": "intent/Who-Is-Searching activates once we detect traffic from this pixel."
}Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand not owned by the account. |
POST /v1/brands/{brand_id}/connections/gbp
Cost: Free
Start a Google Business Profile connection — enables Reviews. The customer completes Google OAuth out of band.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
location_ids | body | string[] / null | No | GBP location ids to connect. |
Request
curl https://api.ai92.ai/v1/brands/b1f0…/connections/gbp \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"location_ids": ["loc_7"]}'Response 200 OK
{
"brand_id": "b1f0…",
"oauth_status": "pending",
"authorize_url": "/v1/oauth/google/start?brand_id=b1f0…",
"note": "Complete Google authorization to enable Reviews/GBP."
}Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand not owned by the account. |
POST /v1/brands/{brand_id}/products
Cost: Free
Add a product (optional) — enables creative image→image generation.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
brand_id | path | string | Yes | The brand id (UUID). |
name | body | string | Yes | Product name. Length 1–255. |
product_url | body | string / null | No | Product page URL. Max length 1024. |
image_url | body | string / null | No | Product image URL. Max length 1024. |
attributes | body | object / null | No | Arbitrary product attributes. |
Request
curl https://api.ai92.ai/v1/brands/b1f0…/products \
-H "Authorization: Bearer ai92_test_51a9c0de…" \
-H "Content-Type: application/json" \
-d '{"name": "Acme CRM Pro", "product_url": "https://acme.com/pro", "image_url": "https://cdn.acme.com/pro.png"}'Response 200 OK
{ "brand_id": "b1f0…", "status": "product_added" }Errors
| Status | Code | When |
|---|---|---|
404 | BRAND_NOT_FOUND | Brand not owned by the account. |
Polling async jobs
Several services (creative, seo/ai-visibility scans, usage export) return a job_id. Poll any async job at:
GET /v1/jobs/{job_id}
Cost: Free
Return the status (and, when finished, the result) of an async job. Returns 404 NOT_FOUND for unknown ids and ids owned by another tenant.
Auth: Bearer
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
job_id | path | string | Yes | The job id returned by the originating endpoint. |
Request
curl https://api.ai92.ai/v1/jobs/f_7c1d0a92… \
-H "Authorization: Bearer ai92_test_51a9c0de…"Response 200 OK
{
"job_id": "f_7c1d0a92…",
"status": "completed",
"service": "creative",
"credits_reserved": 15,
"result": { "asset_id": "ast_nl1", "url": "https://cdn.ai92.ai/ast_nl1.png" }
}Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | Job not found or not owned by the account. |