HTTP API

Complete HTTP reference with parameters. Declarative plan fields: Plan extensions. Luau hooks: Pricing.

Base URL

HTTP: https://api.hume.run · StatsD UDP: api.hume.run:8125

Common headers

HeaderWhenDescription
AuthorizationAll except /healthz, /tenantsBasic base64(tenant_id:api_key)
Content-TypePOST/PUT bodiesapplication/json
Idempotency-KeyDomain writesRequired on catalog, subscription, usage, adjustment, invoice writes
X-Bootstrap-TokenPOST /tenantsRequired when TENANT_CREATE_TOKEN env is set

JSON bodies reject unknown fields. Errors return {"error":"..."}.

Entity identifiers

Entity ids use {prefix}_{ulid} (for example usr_01ARZ3NDEKTSV4RRFFQ69G5FAV). Prefixes identify the type (usr, met, pln, sub, evt, ten, whk, …); the suffix is a monotonic ULID.

Amounts

All money-like fields (monthly_price, amount, unit_amount, total, adjustments, refunds) are integers in abstract billing units. There is no currency field in the API. Display labels (USD, EUR, credits, tokens) and FX are downstream concerns.

Pagination

GET /events and GET /usage accept:

  • limit — max items (0 = empty page)
  • offset — skip from the newest end

Response shape: {"total": N, "events"|"usage": [...]}

Authentication setup

curl
01 curl -s -X POST https://api.hume.run/tenants \
02 -H 'Content-Type: application/json' \
03 -d '{"name":"Acme"}'
curl
01 AUTH_HEADER="Authorization: Basic $(printf '%s' 'ten_01ARZ3NDEKTSV4RRFFQ69G5FAV:YOUR_API_KEY' | base64)"

Endpoints

Quick reference

MethodPathSection
GET/healthzSystem
POST/tenantsSystem
GET/eventsEvents
GET/stateEvents
GET/POST/usersCatalog
GET/POST/metricsCatalog
GET/POST/featuresCatalog
GET/POST/plansCatalog
GET/PUT/plans/{id}/alertsCatalog
POST/pricing/packagesCatalog
POST/pricing/bootstrapCatalog
GET/POST/plans/{id}/featuresCatalog
GET/POST/subscriptionsSubscriptions
GET/subscriptions/{id}Subscriptions
POST/subscriptions/{id}/…Subscriptions
GET/POST/adjustmentsAdjustments
GET/refundsAdjustments
GET/POST/usageUsage
POST/entitlements/checkEntitlements
GET/consumption-flagsEntitlements
POST/eventsEvent ingestion
POST/events/batchEvent ingestion
GET/POST/webhooksWebhooks
DELETE/webhooks/{id}Webhooks
GET/invoicesInvoices
POST/invoices/generateInvoices

System

GET /healthz

AuthNo
Success200 — { status, time }
curl
01 curl -s https://api.hume.run/healthz

POST /tenants

AuthNo
Success201 — tenant credentials

Request body

FieldTypeReqDescription
namestringyesTenant display name

Response

{ tenant: { id, name }, api_key }

curl
01 curl -s -X POST https://api.hume.run/tenants \
02 -H 'Content-Type: application/json' \
03 -d '{"name":"Acme"}'
  • Rate limit: 10 requests/minute when token not configured

Events and state

GET /events

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
limitintnoPage size
offsetintnoSkip from newest

Response

{ total, events[] } — audit journal, newest first (by sequence)

curl
01 curl -s 'https://api.hume.run/events?limit=100&offset=0' \
02 -H "$AUTH_HEADER"
  • Does not accept writes — use REST routes or POST /events for ingestion

GET /state

AuthRequired (Basic)
Success200

Response

Current tenant state (users, metrics, plans, subscriptions, usage aggregates, …)

curl
01 curl -s https://api.hume.run/state \
02 -H "$AUTH_HEADER"

Catalog

GET /users

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
external_idstringnoLookup by CRM / auth system ID (unique per tenant when set)

Response

Array of user objects, or single user when external_id matches

curl
01 curl -s https://api.hume.run/users \
02 -H "$AUTH_HEADER"
03
04 curl -s 'https://api.hume.run/users?external_id=crm-1' \
05 -H "$AUTH_HEADER"

POST /users

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
namestringyesDisplay name
emailstringyesEmail address
external_idstringnoYour CRM / auth system ID
timezonestringnoIANA timezone (e.g. Europe/Berlin); drives billing period boundaries
curl
01 curl -s -X POST https://api.hume.run/users \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: user-alice' \
05 -d '{"name":"Alice","email":"alice@example.com","external_id":"crm-1"}'

GET /users/{id}

AuthRequired (Basic)
Success200

Response

User object including blocked / deleted flags

curl
01 curl -s https://api.hume.run/users/usr_01ARZ3NDEKTSV4RRFFQ69G5FAV \
02 -H "$AUTH_HEADER"

POST /users/{id}/timezone

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
timezonestringyesIANA timezone (Europe/Berlin, America/New_York, …)
curl
01 curl -s -X POST https://api.hume.run/users/usr_01ARZ3NDEKTSV4RRFFQ69G5FAV/timezone \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: tz-alice' \
05 -d '{"timezone":"America/New_York"}'
  • Does not rewrite active subscription billing periods

POST /users/{id}/block

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X POST https://api.hume.run/users/usr_01ARZ3NDEKTSV4RRFFQ69G5FAV/block \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: block-alice'
  • Suspends entitlements and subscription reads (403); usage ingest continues
  • Invoice admin APIs still work — see /docs/billing

POST /users/{id}/unblock

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X POST https://api.hume.run/users/usr_01ARZ3NDEKTSV4RRFFQ69G5FAV/unblock \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: unblock-alice'

DELETE /users/{id}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/users/usr_01ARZ3NDEKTSV4RRFFQ69G5FAV \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: delete-alice'
  • Permanent tombstone; user hidden from GET /users list

GET /metrics

AuthRequired (Basic)
Success200

Response

Array of metric definitions

curl
01 curl -s https://api.hume.run/metrics \
02 -H "$AUTH_HEADER"

POST /metrics

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
codestringyesStable metric key (e.g. api_requests)
namestringyesDisplay name
unitstringyesUnit label (request, gb, …)
curl
01 curl -s -X POST https://api.hume.run/metrics \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: metric-api-requests' \
05 -d '{"code":"api_requests","name":"API Requests","unit":"request"}'

DELETE /metrics/{id}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/metrics/met_01J8ZK5BQY8XQ9R2M4V7W3N6T1 \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: delete-metric-api-requests'
  • Returns 400 if any plan still references the metric
  • monthly_tracked_users cannot be deleted
  • Event: metric.deleted

GET /features

AuthRequired (Basic)
Success200

Response

Array of feature catalog entries

curl
01 curl -s https://api.hume.run/features \
02 -H "$AUTH_HEADER"

POST /features

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
codestringyesFeature key
namestringyesDisplay name
descriptionstringnoOptional description
curl
01 curl -s -X POST https://api.hume.run/features \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: feature-exports' \
05 -d '{"code":"exports","name":"Exports","description":"CSV exports"}'

DELETE /features/{id}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/features/fea_01J8ZK5BQY8XQ9R2M4V7W3N6T0 \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: delete-feature-exports'
  • Returns 400 if any plan has the feature in binding history
  • Event: feature.deleted

GET /plans

AuthRequired (Basic)
Success200

Response

Array of plans with metric bindings

curl
01 curl -s https://api.hume.run/plans \
02 -H "$AUTH_HEADER"

POST /plans

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
codestringyesPlan key
namestringyesDisplay name
monthly_priceintyesBase fee in abstract units
metrics[]objectyesBind existing metrics by metric_id
features[]objectnoOptional feature bindings
pricing_luastringnoLuau plan manifest (field name is historical)
metric_groups[]objectnoShared included/tiers across metrics — see Plan extensions
usage_poolobjectnoSingle shared included bucket for listed metrics
classifyobjectnoJSON routes and reject rules at ingest
limits[]objectnoHard caps per metric or group (action: reject)
alerts[]objectnoThreshold notifications (action: webhook) — see Plan alerts
minimum_monthly_chargeintnoTop-up when subtotal is below minimum
commitment_unitsobjectnometric_code + min usage commitment
included_rolloverobjectnomax_periods, cap_units
seat_pricingobjectnoincluded_seats, overage_per_seat
billing_intervalstringnomonth (default) or year
annual_discount_percentintno0–99 when billing_interval is year
line_labelsobjectnobase, overage invoice descriptions
curl
01 curl -s -X POST https://api.hume.run/plans \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: plan-basic' \
05 -d '{"code":"basic","name":"Basic","monthly_price":1000,"metrics":[{"metric_id":"met_01J8ZK5BQY8XQ9R2M4V7W3N6T1","included_units":500,"tiers":[{"from":501,"to":2000,"price_per_unit":4}]}]}'
  • metrics[].metric_id required
  • tiers[] and overage_unit_price are mutually exclusive per metric
  • stairstep cannot combine with tiers or overage_unit_price
  • Full extension reference: /docs/pricing/plan-extensions

POST /pricing/packages

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
codestringyesPlan key
namestringyesDisplay name
monthly_priceintyesBase fee
metrics[]objectyesCreates metrics + binds to plan
features[]objectnoCreates features + binds to plan
pricing_luastringnoLuau manifest — skips built-in JSON billing when set
metric_groups[]objectnoShared included/tiers across metrics — see Plan extensions
usage_poolobjectnoSingle shared included bucket for listed metrics
classifyobjectnoJSON routes and reject rules at ingest
limits[]objectnoHard caps per metric or group (action: reject)
alerts[]objectnoThreshold notifications (action: webhook) — see Plan alerts
minimum_monthly_chargeintnoTop-up when subtotal is below minimum
commitment_unitsobjectnometric_code + min usage commitment
included_rolloverobjectnomax_periods, cap_units
seat_pricingobjectnoincluded_seats, overage_per_seat
billing_intervalstringnomonth (default) or year
annual_discount_percentintno0–99 when billing_interval is year
line_labelsobjectnobase, overage invoice descriptions

Response

{ plan, metrics[], features[], pricing? }

curl
01 curl -s -X POST https://api.hume.run/pricing/packages \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: pricing-pro' \
05 -d '{"code":"pro","name":"Pro","monthly_price":2500,"metrics":[{"metric_code":"api_requests","metric_name":"API Requests","metric_unit":"request","included_units":500}],"features":[{"feature_code":"exports","feature_name":"Exports","enabled":true}]}'
  • metrics[].metric_id — Existing metric ID (low-level plans)
  • metrics[].metric_code — Metric code — creates metric in packages
  • metrics[].metric_name — Display name when creating via package
  • metrics[].metric_unit — Unit label when creating via package
  • metrics[].included_units — Free units per billing period
  • metrics[].overage_unit_price — Per-unit price after included (if no tiers)
  • metrics[].overage_description — Custom overage line label
  • metrics[].tiers[] — Volume tiers: from, to (null = open), price_per_unit
  • metrics[].pricing_model — stairstep or empty
  • metrics[].block_size — Stairstep: units per block
  • metrics[].block_price — Stairstep: price per block
  • metrics[].aggregation — Invoice window: sum (default), max, last
  • Without pricing_lua, built-in billing uses extension fields — see /docs/pricing/plan-extensions

POST /pricing/bootstrap

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
plans[]objectyesArray of plan definitions — same fields as /pricing/packages
curl
01 curl -s -X POST https://api.hume.run/pricing/bootstrap \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: bootstrap-v1' \
05 -d '{"plans":[{"code":"free","price":0,"metrics":{"api_requests":{"included":1000}}},{"code":"pro","price":5000,"metrics":{"api_requests":{"included":100000,"overage":1}},"features":["exports"]}]}'
  • Compact syntax supported: price (alias for monthly_price), metrics as map code→{included, overage}, features as string[]
  • Shared metrics created once and reused across plans
  • See /docs/pricing — bootstrap example

DELETE /plans/{id}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0 \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: delete-plan-basic'
  • Returns 400 if any subscription references the plan (current plan_id or pending_change)
  • After delete the plan code can be reused
  • Event: plan.deleted

Plan edit (unused plans only)

When no subscription references a plan, you can remove metrics/features and manage ingest limits. Removing a metric also clears groups, pool, limits, classify, commitment, and seat bindings for that code.

DELETE /plans/{id}/metrics/{metric_id}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/metrics/met_01J8ZK5BQY8XQ9R2M4V7W3N6T1 \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: plan-drop-metric'
  • Event: plan.metric_removed

DELETE /plans/{id}/features/{feature_id}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/features/fea_01J8ZK5BQY8XQ9R2M4V7W3N6T0 \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: plan-drop-feature'
  • Clears binding history entry; event: plan.feature_removed

GET /plans/{id}/limits

AuthRequired (Basic)
Success200

Response

Current limits[] on the plan

curl
01 curl -s https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/limits \
02 -H "$AUTH_HEADER"

POST /plans/{id}/limits

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
scopestringyesmetric | metric_group
metric_codestringnoWhen scope is metric
groupstringnoWhen scope is metric_group
max_per_periodintyesHard cap in current subscription period
actionstringyesreject (only supported action)
curl
01 curl -s -X POST https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/limits \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: plan-add-limit' \
05 -d '{"scope":"metric","metric_code":"api_requests","max_per_period":50000,"action":"reject"}'
  • Appends a limit; event: plan.limits_updated

PUT /plans/{id}/limits

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
limits[]objectyesReplace entire limits array
curl
01 curl -s -X PUT https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/limits \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: plan-set-limits' \
05 -d '[{"scope":"metric_group","group":"tokens","max_per_period":1000000,"action":"reject"}]'

PUT /plans/{id}/limits/{index}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
scopestringyesmetric | metric_group
max_per_periodintyesUpdated cap
actionstringyesreject
curl
01 curl -s -X PUT https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/limits/0 \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: plan-update-limit' \
05 -d '{"scope":"metric","metric_code":"api_requests","max_per_period":80000,"action":"reject"}'

DELETE /plans/{id}/limits/{index}

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X DELETE https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/limits/0 \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: plan-remove-limit'

Plan alerts

Alerts emit a durable usage.threshold_crossed event once per billing period when usage crosses percent of the baseline. Baseline is baseline_units if set, else a matching hard limits[].max_per_period, else included_units. Engine action is always webhook — email and other side effects belong to receivers. Allowed on plans that are already in use.

GET /plans/{id}/alerts

AuthRequired (Basic)
Success200

Response

Current alerts[] on the plan

curl
01 curl -s https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/alerts \
02 -H "$AUTH_HEADER"

PUT /plans/{id}/alerts

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
scopestringyesmetric | metric_group
metric_codestringnoWhen scope is metric
groupstringnoWhen scope is metric_group
percentintyes1–100; fire when period usage crosses this % of baseline
baseline_unitsintnoOptional explicit baseline; otherwise limit or included_units
actionstringnowebhook (default)
curl
01 curl -s -X PUT https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/alerts \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: plan-set-alerts' \
05 -d '[{"scope":"metric","metric_code":"api_requests","percent":80,"baseline_units":100,"action":"webhook"}]'
  • Body is a JSON array replacing the entire alerts list; event: plan.alerts_updated

GET /plans/{id}/features

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
atRFC3339noPoint-in-time feature bindings
curl
01 curl -s 'https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/features?at=2026-07-20T12:00:00Z' \
02 -H "$AUTH_HEADER"

POST /plans/{id}/features

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
feature_codestringnoFeature code (or feature_id)
feature_idstringnoFeature ID alternative
enabledboolyesGrant or revoke
effective_fromRFC3339noDelayed activation
curl
01 curl -s -X POST https://api.hume.run/plans/pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0/features \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: plan-exports' \
05 -d '{"feature_code":"exports","enabled":true}'

Subscriptions

GET /subscriptions

AuthRequired (Basic)
Success200

Response

All subscriptions with current lifecycle state

curl
01 curl -s https://api.hume.run/subscriptions \
02 -H "$AUTH_HEADER"

GET /subscriptions/{id}

AuthRequired (Basic)
Success200 / 404

Response

Single subscription snapshot at current time

curl
01 curl -s https://api.hume.run/subscriptions/sub_01J8ZK5BQY8XQ9R2M4V7W3N6T3 \
02 -H "$AUTH_HEADER"

POST /subscriptions

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
user_idstringyesBillable user
plan_idstringyesPlan to subscribe to
trial_daysintnoTrial length (mutually exclusive with trial_ends_at)
trial_ends_atRFC3339noExplicit trial end
curl
01 curl -s -X POST https://api.hume.run/subscriptions \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: sub-1' \
05 -d '{"user_id":"usr_01ARZ3NDEKTSV4RRFFQ69G5FAV","plan_id":"pln_01J8ZK5BQY8XQ9R2M4V7W3N6T0","trial_days":14}'

POST /subscriptions/{id}/change-plan

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
plan_idstringyesTarget plan
policystringyesprorate_immediately | refund_immediately | effective_next_cycle

Response

{ subscription, adjustment?, refund? }

curl
01 curl -s -X POST https://api.hume.run/subscriptions/sub_01J8ZK5BQY8XQ9R2M4V7W3N6T3/change-plan \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: change-1' \
05 -d '{"plan_id":"pln_01J8ZK5BQY8XQ9R2M4V7W3N6T4","policy":"prorate_immediately"}'
  • prorate_immediately → separate adjustment.posted (kind: prorate)
  • refund_immediately → separate refund.issued for unused time credit
  • effective_next_cycle → subscription.plan_change_scheduled

POST /subscriptions/{id}/pause

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X POST https://api.hume.run/subscriptions/sub_01J8ZK5BQY8XQ9R2M4V7W3N6T3/pause \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: pause-1'
  • No request body
  • Paused time excluded from base fee

POST /subscriptions/{id}/resume

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200
curl
01 curl -s -X POST https://api.hume.run/subscriptions/sub_01J8ZK5BQY8XQ9R2M4V7W3N6T3/resume \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: resume-1'

POST /subscriptions/{id}/cancel-at-period-end

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Query parameters

FieldTypeReqDescription
enabledboolnotrue (default) to cancel at period end; false to clear
curl
01 curl -s -X POST 'https://api.hume.run/subscriptions/sub_01J8ZK5BQY8XQ9R2M4V7W3N6T3/cancel-at-period-end?enabled=true' \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: cancel-1'

Adjustments and refunds

Manual billing corrections are first-class events, not embedded fields on other records. Amounts are signed integers in billing units (negative = credit, positive = charge). There is no currency code. kind: prorate (plan change), credit, debit (manual; inferred from sign if omitted). They appear as invoice lines when created_at falls inside the invoice window.

POST /adjustments

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
subscription_idstringyesSubscription to adjust
amountintyesSigned amount in abstract units (negative = credit)
kindstringnoprorate | credit | debit (inferred from sign if omitted)
descriptionstringyesLine description on invoice

Response

Adjustment record — emits adjustment.posted

curl
01 curl -s -X POST https://api.hume.run/adjustments \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: adj-1' \
05 -d '{"subscription_id":"sub_01J8ZK5BQY8XQ9R2M4V7W3N6T3","amount":-500,"kind":"credit","description":"Goodwill credit"}'
  • Response fields: id, subscription_id, user_id, amount, description, source_event_id?, created_at
  • Plan changes with refund_immediately append refund.issued automatically (amount always positive; invoice line is negative)

GET /adjustments

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
user_idstringnoFilter by user (optional)

Response

Array of adjustment records

curl
01 curl -s 'https://api.hume.run/adjustments?user_id=usr_01ARZ3NDEKTSV4RRFFQ69G5FAV' \
02 -H "$AUTH_HEADER"

GET /refunds

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
user_idstringnoFilter by user (optional)

Response

Array of refund records — fields: id, subscription_id, user_id, amount, reason, source_event_id?, created_at

curl
01 curl -s 'https://api.hume.run/refunds?user_id=usr_01ARZ3NDEKTSV4RRFFQ69G5FAV' \
02 -H "$AUTH_HEADER"

Usage

GET /usage

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
user_idstringnoFilter by user
metric_idstringnoFilter by metric
fromRFC3339noRange start (inclusive)
toRFC3339noRange end (exclusive)
limitintnoPagination
offsetintnoPagination (newest first)

Response

{ total, usage[] }

curl
01 curl -s 'https://api.hume.run/usage?user_id=usr_01ARZ3NDEKTSV4RRFFQ69G5FAV&limit=50' \
02 -H "$AUTH_HEADER"

POST /usage

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
user_idstringyesUser consuming the metric
metric_idstringyesMetric ID
quantityintyesUnits consumed
created_atRFC3339noEvent time; max +24h in future
curl
01 curl -s -X POST https://api.hume.run/usage \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: usage-1' \
05 -d '{"user_id":"usr_01ARZ3NDEKTSV4RRFFQ69G5FAV","metric_id":"met_01J8ZK5BQY8XQ9R2M4V7W3N6T1","quantity":150}'
  • Shorthand for usage.recorded event
  • Plan may apply JSON classify, limits, then Luau classify before commit — metric may be rerouted or rejected with 400

Entitlements

POST /entitlements/check

AuthRequired (Basic)
Success200

Request body

FieldTypeReqDescription
user_idstringyesUser to check
feature_codestringnoFeature key (or feature_id)
feature_idstringnoFeature ID alternative
atRFC3339noDefaults to now

Response

{ allowed, reason, subscription_status, plan_id, checked_at, … }

curl
01 curl -s -X POST https://api.hume.run/entitlements/check \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -d '{"user_id":"usr_01ARZ3NDEKTSV4RRFFQ69G5FAV","feature_code":"exports"}'

GET /consumption-flags

AuthRequired (Basic)
Success200

Query parameters

FieldTypeReqDescription
user_idstringyesUser to inspect
atRFC3339noHistorical point-in-time

Response

Flags: usage.near_limit, usage.over_limit, usage.current_tier

curl
01 curl -s 'https://api.hume.run/consumption-flags?user_id=usr_01ARZ3NDEKTSV4RRFFQ69G5FAV' \
02 -H "$AUTH_HEADER"

Event ingestion (optional)

For normal use, prefer the dedicated REST routes (POST /users, POST /usage, …). POST /events is a generic envelope if an external system prefers one JSON shape. For usage.recorded, POST /usage is equivalent and simpler. POST /events/batch accepts many events in one request, with a per-item result (207 if some rows failed).

POST /events

AuthRequired (Basic)
Success202 Accepted

Request body

FieldTypeReqDescription
event_idstringyesIdempotency key for the log
typestringyesEvent type (see table below)
sourcestringyesOriginating system
occurred_atRFC3339yesWhen it happened; max +24h in future
dataobjectyesType-specific payload
curl
01 curl -s -X POST https://api.hume.run/events \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -d '{"event_id":"evt_01J8ZK5BQY8XQ9R2M4V7W3N6T5","type":"usage.recorded","source":"api","occurred_at":"2026-07-01T12:00:00Z","data":{"user_id":"usr_01ARZ3NDEKTSV4RRFFQ69G5FAV","metric_code":"api_requests","quantity":42}}'
  • Catalog events (user.created, metric.created, …) create entities — e.g. user.created requires data.user_name and data.user_email; data.user_id is stored as external_id

POST /events/batch

AuthRequired (Basic)
Success202 / 207 Multi-Status

Request body

FieldTypeReqDescription
events[]EventInputyesArray of events, applied sequentially

Response

{ results: [{ index, record? } | { index, error }], accepted, failed } — HTTP 207 if any row failed

curl
01 curl -s -X POST https://api.hume.run/events/batch \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -d '{"events":[{"event_id":"evt_01J8ZK5BQY8XQ9R2M4V7W3N6T5","type":"usage.recorded","source":"api","occurred_at":"2026-07-01T12:00:00Z","data":{"user_id":"usr_01ARZ3NDEKTSV4RRFFQ69G5FAV","metric_id":"met_01J8ZK5BQY8XQ9R2M4V7W3N6T1","quantity":5}}]}'

Event types

Writable types for POST /events and POST /events/batch:

typeREST equivalentNotes
user.createdPOST /usersrequires data.user_name, data.user_email
user.blockedPOST /users/{id}/blocksuspends read access; usage ingest continues
user.unblockedPOST /users/{id}/unblockrestores read access
user.deletedDELETE /users/{id}permanent tombstone
user.timezone_updatedPOST /users/{id}/timezoneIANA timezone for local billing
metric.createdPOST /metrics
metric.deletedDELETE /metrics/{id}only when no plan references the metric
feature.createdPOST /features
feature.deletedDELETE /features/{id}only when no plan binding history references the feature
plan.createdPOST /plans
plan.deletedDELETE /plans/{id}only when no subscription references the plan
plan.metric_removedDELETE /plans/{id}/metrics/{metric_id}only when plan has no subscriptions
plan.feature_removedDELETE /plans/{id}/features/{feature_id}clears binding history; plan must have no subscriptions
plan.limits_updatedPUT/POST /plans/{id}/limits, PUT/DELETE …/limits/{index}replace, add, update, or remove ingest limits on unused plans
plan.alerts_updatedPUT /plans/{id}/alertsreplace threshold alerts (allowed on plans in use)
plan.feature_enabledPOST /plans/{id}/featuresenabled: true
plan.feature_disabledPOST /plans/{id}/featuresenabled: false
usage.recordedPOST /usage
usage.threshold_crossed(side effect of usage + plan alerts)once per period per alert when crossing percent of baseline
subscription.createdPOST /subscriptions
subscription.plan_changedPOST …/change-planmay emit adjustment.posted or refund.issued
subscription.plan_change_scheduledchange-plan effective_next_cycle
subscription.pausedPOST …/pause
subscription.resumedPOST …/resume
subscription.cancel_at_period_end_setcancel-at-period-end enabled=true
subscription.cancel_at_period_end_clearedcancel-at-period-end enabled=false
adjustment.postedPOST /adjustmentsmanual or plan-change proration
refund.issued(side effect of refund plan change)credit for unused subscription time

Lifecycle events

Emitted automatically on subscription state transitions (not writable via public REST):

typeWhen
subscription.activatedTrial ends or subscription becomes billable
subscription.canceledSubscription is canceled (immediate or at period end)
subscription.period_rolledBilling period advances

Webhooks

Register an outbound URL per tenant. On each committed event, the service POSTs the EventRecord JSON asynchronously. Failed deliveries retry 3 times with a 2s pause.

GET /webhooks

AuthRequired (Basic)
Success200

Response

{ webhooks: WebhookEndpoint[] } — id, url, events, created_at (secret is never returned)

curl
01 curl -s https://api.hume.run/webhooks \
02 -H "$AUTH_HEADER"

POST /webhooks

AuthRequired (Basic)
Success201

Request body

FieldTypeReqDescription
urlstringyeshttp or https endpoint URL
eventsstring[]yesEvent types to forward, or ["*"] for all
secretstringnoHMAC signing secret; server generates one if omitted

Response

WebhookEndpoint + secret — secret is returned only in this response

curl
01 curl -s -X POST https://api.hume.run/webhooks \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -d '{"url":"https://your-app.example/webhooks/billing","events":["subscription.created","subscription.canceled"]}'
  • Same event types as GET /events (user.created, usage.recorded, subscription.canceled, …)

DELETE /webhooks/{id}

AuthRequired (Basic)
Success204 / 404
curl
01 curl -s -X DELETE https://api.hume.run/webhooks/whk_01J8ZK5BQY8XQ9R2M4V7W3N6T2 \
02 -H "$AUTH_HEADER"

Delivery payload

Each delivery is a POST with Content-Type: application/json. The body is the committed EventRecord — the same shape as entries in GET /events.

json
{
  "sequence": 12,
  "event_id": "evt_01J8ZK5BQY8XQ9R2M4V7W3N6T5",
  "type": "subscription.created",
  "source": "http",
  "occurred_at": "2026-07-01T12:00:00Z",
  "accepted_at": "2026-07-01T12:00:00Z",
  "data": { "...": "..." },
  "result": { "subscription": { "...": "..." } }
}

Signature verification

POST /webhooks returns a secret once. Store it on the receiver and verify every delivery before processing. Each delivery includes:

HeaderMeaning
Billing-Webhook-Signaturet=<unix>,v1=<hex>

Compute v1 = HMAC-SHA256(secret, "<unix>.<raw body>") and compare with a constant-time function. Reject requests older than ~5 minutes (replay protection).

On retry the body and signature timestamp are identical — deduplicate by event_id and return 2xx without re-processing duplicates.

Subscribable event types

events lists event types to forward, or ["*"] for all. The same types as in the event types apply, including lifecycle events:

typeWhen
user.createdUser created
user.blockedUser suspended (non-payment)
user.unblockedUser restored
user.deletedUser permanently removed
metric.createdMetric created
feature.createdFeature created
plan.createdPlan created
plan.feature_enabled / plan.feature_disabledPlan feature binding changed
usage.recordedUsage recorded (HTTP, StatsD, or synthetic)
usage.threshold_crossedPlan alert threshold crossed (once per period)
plan.alerts_updatedPlan alerts replaced
subscription.createdSubscription created
subscription.plan_changedPlan changed immediately
subscription.plan_change_scheduledPlan change queued for next cycle
subscription.paused / subscription.resumedPause / resume
subscription.cancel_at_period_end_set / subscription.cancel_at_period_end_clearedCancel-at-period-end toggled
subscription.activatedTrial ended or subscription became billable
subscription.canceledSubscription canceled
subscription.period_rolledBilling period advanced
adjustment.postedAdjustment recorded
refund.issuedRefund recorded
invoice.generatedDraft invoice created
invoice.finalizedInvoice amounts locked
invoice.paidPayment recorded
invoice.voidedInvoice voided

Invoices

GET /invoices

AuthRequired (Basic)
Success200

Response

Array of invoices (draft, finalized, paid, void)

curl
01 curl -s https://api.hume.run/invoices \
02 -H "$AUTH_HEADER"

GET /invoices/{id}

AuthRequired (Basic)
Success200

Response

Single invoice

curl
01 curl -s https://api.hume.run/invoices/inv_01ARZ3NDEKTSV4RRFFQ69G5FAV \
02 -H "$AUTH_HEADER"

POST /invoices/generate

AuthRequired (Basic)
Idempotency-KeyRequired header
Success201

Request body

FieldTypeReqDescription
user_idstringyesBillable user
period_startRFC3339yesInvoice window start
period_endRFC3339yesInvoice window end (exclusive)

Response

Draft invoice JSON with line items in abstract units

curl
01 curl -s -X POST https://api.hume.run/invoices/generate \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: invoice-jan' \
05 -d '{"user_id":"usr_01ARZ3NDEKTSV4RRFFQ69G5FAV","period_start":"2026-07-01T00:00:00Z","period_end":"2026-08-01T00:00:00Z"}'
  • Status is draft — finalize before collection
  • Invoices for any window, including past months — period rollover does not break historical calculations
  • adjustment.posted and refund.issued in window → type: adjustment / refund lines (refunds are negative amounts)
  • Base fee proportional to active time — trials and pauses excluded; plan segments billed separately
  • Usage billed within periods; included_units and tier boundaries scaled for partial windows
  • Same metric across multiple subscriptions for one user billed once
  • Plans with Luau pricing replace built-in lines per billing period
  • Plans without Luau pricing use JSON billing (groups, pools, tiers, stairstep, seats, minimum, commitment) — see Plan extensions
  • Classify (JSON or Luau) runs only at usage ingest, not during invoicing

POST /invoices/{id}/finalize

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Response

Invoice with status finalized

curl
01 curl -s -X POST https://api.hume.run/invoices/inv_01ARZ3NDEKTSV4RRFFQ69G5FAV/finalize \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: invoice-jan-finalize'
  • Works while user is blocked — tenant admin only

POST /invoices/{id}/mark-paid

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Request body

FieldTypeReqDescription
payment_refstringyesPSP reference (e.g. stripe_pi_…)

Response

Invoice with status paid

curl
01 curl -s -X POST https://api.hume.run/invoices/inv_01ARZ3NDEKTSV4RRFFQ69G5FAV/mark-paid \
02 -H 'Content-Type: application/json' \
03 -H "$AUTH_HEADER" \
04 -H 'Idempotency-Key: invoice-jan-paid' \
05 -d '{"payment_ref":"stripe_pi_3ABC"}'
  • Call after PSP confirms payment; works while user is blocked

POST /invoices/{id}/void

AuthRequired (Basic)
Idempotency-KeyRequired header
Success200

Response

Invoice with status void

curl
01 curl -s -X POST https://api.hume.run/invoices/inv_01ARZ3NDEKTSV4RRFFQ69G5FAV/void \
02 -H "$AUTH_HEADER" \
03 -H 'Idempotency-Key: invoice-jan-void'
  • Draft or finalized only — not paid

StatsD ingestion

UDP packet format:

text
billing.<tenant_id>.<api_key>.user.<user_id>.<metric_code>:<value>|c
curl
01 printf 'billing.ten_01ARZ3NDEKTSV4RRFFQ69G5FAV.sk_live_xxx.user.usr_01ARZ3NDEKTSV4RRFFQ69G5FAV.api_requests:42|c\n' | nc -w1 -u api.hume.run 8125

|g gauge type also accepted — value is still treated as usage quantity. Optional idempotency tag: |#event_id:evt-123.