An event records something that happened in your account, such as a job that ended or a balance that ran low. Each one
is delivered to the webhook endpoints subscribed to its type, and GET /v1/events lists every
event for 30 days, whether or not a webhook endpoint received it.
| Route | What it does | Key scope |
|---|---|---|
GET /v1/events | List events, newest first | account:read |
GET /v1/events/{id} | Get an event | account:read |
POST /v1/events/{id}/redeliver | Deliver an event again | admin |
The event object
The body of a webhook delivery is the event object, exactly as GET /v1/events/{id} returns it:
| Field | Type | Description |
|---|---|---|
object | string | Always event. One of event |
id | string | The event ID (evt_…): the webhook-id header of every delivery, the same on retries. Deduplicate on it. |
type | string | The event type (event types table); new types may be added. |
timestamp | string (date-time) | When it happened. |
data | any | The type's data object (small: a summary plus links). |
Event types
| Type | When | data |
|---|---|---|
job.completed | A job ended with every item ended (check request_counts: items fail individually). | JobEventData |
job.canceled | A canceled job ended: its running items finished. | JobEventData |
job.expired | A job reached its deadline; its unfinished items expired. | JobEventData |
credits.low | A balance change took the balance from above the account's low_balance_threshold to at or below it (once per crossing; the owner is emailed too). | CreditsLowData |
credits.depleted | The balance reached 0 (once, until credits are added). The owner is also emailed, at most once a day, whatever the notification settings say: it is how a leaked key is noticed. | CreditsDepletedData |
auto_topup.succeeded | Auto top-up bought its pack: the charge was paid and the pack's credits added (once). | AutoTopupCharge |
auto_topup.failed | An auto top-up charge failed (declined, or the bank asked the cardholder to confirm it): nothing was charged, auto top-up turned off and the owner was emailed why. It never retries: a person turns it back on. | AutoTopupCharge |
auto_topup.skipped | The balance crossed the auto top-up threshold, but buying the pack would have passed the monthly cap (the owner is emailed, once a month) or the account's limit per {trust_window_days} days (the owner is emailed how to lift it, once per {trust_window_days} days): nothing was charged. Auto top-up stays on. | AutoTopupCharge |
plan.renewed | A monthly plan's payment went through: its credits were added and what was left of last month's ended (the first payment of a plan too). | PlanEventData |
plan.changed | An upgrade was paid: the new plan and a new month started now, and its credits were added. | PlanEventData |
plan.payment_failed | A monthly plan's renewal payment failed: no new credits were added and the owner was emailed. Stripe retries it; the plan ends if the retries fail. | PlanEventData |
plan.ended | A monthly plan ended (canceled, or its payment retries failed): the monthly credits left ended with it, pack and top-up credits stay, and the account is pay as you go. | PlanEventData |
api_key.spend_limit_reached | An API key's request (or job) was refused for its spend limit, the first time in the limit's interval. | SpendLimitReachedData |
api_key.created | An API key was created (not an account's first keys). | APIKeyEventData |
api_key.revoked | An API key was revoked. | APIKeyEventData |
api_key.secret_rotated | An API key's secret was rotated: the key has a new secret (prefix), the old one works until its overlap ends. | APIKeyEventData |
webhook.test | Sent by POST /v1/webhook_endpoints/{id}/test to that endpoint only. | WebhookTestData |
New types may be added; ignore the ones you don't handle. Subscribe an endpoint to a family with a wildcard (job.*)
or to everything with *, and filter GET /v1/events the same way with its type parameter.
Job events
job.completed, job.canceled and job.expired carry the job's summary and links, never its
results:
| Field | Type | Description |
|---|---|---|
object | string | One of job |
id | string | |
status | string | One of queued, running, canceling, completed, canceled, expired |
request_counts | object | |
request_counts.total | integer | |
request_counts.queued | integer | Not started. |
request_counts.running | integer | Started (fetching, waiting for a retry or walking pages). |
request_counts.succeeded | integer | |
request_counts.failed | integer | |
request_counts.canceled | integer | |
request_counts.expired | integer | |
credits | object | |
credits.max | integer | max_credits: the most the job may spend. |
credits.reserved | integer | Credits its running pages may still use (0 once the job ends). |
credits.charged | integer | Credits charged so far, each page once (items' prices; failed items are free). |
metadata | object | |
created_at | string (date-time) | |
finished_at | string (date-time) or null | |
url | string | GET this (with your API key) for the job. |
results_url | string | Every result as JSON Lines, the job's signed results_url: it works without the API key for 1 hour after the event; GET url for a fresh one. Reading the event with a key without jobs:read, this is url instead: the link is a job's results, whatever key holds it. |
url is the job (GET it with your API key); results_url is the job's own signed JSON Lines link to every result,
which works without the key until it expires (GET url for a fresh one). GET /v1/events and GET /v1/events/{id}
answer it only to a key that can read jobs (jobs:read); for any other it holds url.
Account events
The balance and spend events come from the account itself,
api_key.created, api_key.revoked and api_key.secret_rotated from managing keys, and
webhook.test from a test delivery. Their data:
credits.low
| Field | Type | Description |
|---|---|---|
balance | integer | The balance after the change that crossed the threshold. |
low_balance_threshold | integer | The account's low_balance_threshold at the time. |
credits.depleted
| Field | Type | Description |
|---|---|---|
balance | integer | The balance: 0. |
auto_topup.succeeded, auto_topup.failed, auto_topup.skipped
| Field | Type | Description |
|---|---|---|
object | string | Always auto_topup_charge. One of auto_topup_charge |
id | string | Top-up ID (atu_…): the ref of its credit_transaction, and on the Stripe payment's metadata. |
status | string | processing (the charge is under way), succeeded (paid: its credits were added, once), failed (nothing charged: auto top-up turned off, see reason) or skipped (it would have passed the monthly cap or the 7-day limit: nothing charged, auto top-up stays on). One of processing, succeeded, failed, skipped |
pack | string | The credit pack (catalog pack ID). |
credits | integer | Credits the pack adds. |
amount_total | integer or null | Charged, tax included, in cents of currency; null when nothing was charged or before it is known. |
amount_tax | integer or null | The tax in amount_total (Stripe Tax: only where SocialTap collects it); null when amount_total is. |
currency | string | amount_total's currency (ISO 4217). One of usd |
reason | string or null | Why it failed (the off_reason it set) or was skipped (monthly_cap, trust_limit: it would have passed rolling_limit_cents); null otherwise. One of card_declined, authentication_required, no_card, billing_address_required, payment_failed, disputed, monthly_cap, trust_limit |
message | string or null | What the bank or Stripe said, for people; null when reason is. |
created_at | string (date-time) | When the balance crossed the threshold, RFC 3339 UTC. |
plan.renewed, plan.changed, plan.payment_failed, plan.ended
| Field | Type | Description |
|---|---|---|
plan | string | The monthly plan (catalog plan ID). |
credits | integer | Credits this event added; 0 when it added none. |
current_period_end | string (date-time) or null | When the plan renews next, RFC 3339 UTC; null when it has ended. |
api_key.spend_limit_reached
| Field | Type | Description |
|---|---|---|
api_key_id | string | The key (key_…) whose spend limit was reached. |
api_key_name? | string or null | Its name (null if unreadable; absent from events sent before 2026-10-31). |
spend_limit | object | Its spend limit. |
spend_limit.amount | integer | Most credits the key may spend per interval. |
spend_limit.interval | string | Calendar day or month, UTC. One of day, month |
used | integer | Credits the key had used this interval (spent plus held by its requests in flight). |
resets_at | string (date-time) | Start of the next interval, RFC 3339 UTC: the key can spend again then. |
api_key.created, api_key.revoked, api_key.secret_rotated
| Field | Type | Description |
|---|---|---|
api_key_id | string | The key (key_…). |
name | string | Its label. |
prefix | string | The secret's first characters, to recognize it. |
webhook.test
| Field | Type | Description |
|---|---|---|
webhook_endpoint_id | string |