Usage, quota & audit
Four read-only endpoints answer the questions every operator asks: Who am I and how much credit is left? What are my limits and how close am I? Where did the money go? Who changed what?
All paths under
/v1/.Authorization: Bearer <api-key>required. All money is in cents (USD). Timestamps are ISO-8601.
| Endpoint | Answers | Shape |
|---|---|---|
GET /v1/account | Who am I, balance, limits | single object |
GET /v1/quota | Limits and live usage against them | single object |
GET /v1/usage | Every credit and debit, line by line | paginated list |
GET /v1/audit | Who did what, when, from where | paginated list |
Account snapshot
One call returns the tenant, the live balance, and the quota caps — the same three blocks you'd otherwise fetch separately.
GET /v1/account
{
"tenant": {
"id": "…",
"name": "Yourco Voice",
"slug": "yourco-voice",
"status": "active",
"currency": "USD",
"metadata": {},
"webhook_failover_url": "https://yourapp.com/voepy/failover",
"company": { "legal_name": "Yourco, Inc.", "country": "US", "…": "…" },
"email_verified_at": "2026-05-01T09:14:00.000Z",
"created_at": "2026-05-01T09:00:00.000Z",
"updated_at": "2026-05-12T14:00:00.000Z"
},
"balance": {
"balance_cents": 12450,
"credit_limit_cents": 0,
"auto_recharge_threshold_cents": 1000,
"auto_recharge_amount_cents": 5000,
"updated_at": "2026-05-12T14:33:00.880Z"
},
"quota": {
"daily_spend_cap_cents": 50000,
"monthly_spend_cap_cents": 1000000,
"concurrent_call_limit": 25,
"concurrent_call_limit_inbound": 25,
"messages_per_second": 1,
"blocked_country_codes": [],
"allowed_country_codes": ["US", "CA"],
"blocked_prefixes": [],
"max_destination_rate_cents": 50,
"allow_caller_id_forwarding": false,
"updated_at": "2026-05-10T00:00:00.000Z"
}
}
status is pending_verification, active, suspended, or
closed. New outbound calls are rejected unless the tenant is
active.
The balance block:
| Field | Meaning |
|---|---|
balance_cents | Remaining prepaid credit. Signed — can go negative if a credit limit is set. |
credit_limit_cents | How far below zero you may go before calls are blocked. 0 for most tenants. |
auto_recharge_threshold_cents | Recharge fires at/under this; null if auto-recharge is off. |
auto_recharge_amount_cents | How much each recharge adds; null if off. |
updated_at | Last time the balance moved. |
Calls are blocked with 402 balance_insufficient once
balance_cents + credit_limit_cents reaches zero. Fund the balance
with a top-up — see Billing.
Quota
GET /v1/account returns the quota caps. GET /v1/quota returns
the caps plus where you stand against them right now — today's
spend, the month's spend, and how many calls are in flight. It reads
live (no cache) and is the exact snapshot the pre-flight gates
evaluate before every call, so the numbers you see are the numbers
that decide whether the next call goes through.
GET /v1/quota
{
"daily_spend_cap_cents": 50000,
"daily_spent_cents": 11920,
"monthly_spend_cap_cents": 1000000,
"monthly_spent_cents": 384210,
"concurrent_call_limit": 25,
"in_flight_calls": 3,
"max_destination_rate_cents": 50,
"blocked_country_codes": [],
"allowed_country_codes": ["US", "CA"],
"blocked_prefixes": ["1900"],
"allow_caller_id_forwarding": false,
"balance_cents": 12450,
"credit_limit_cents": 0,
"tenant_status": "active",
"as_of": "2026-05-12T14:33:01.000Z"
}
This is a flat object — live usage sits alongside the caps it counts against, not nested under them.
Caps
| Field | Gates |
|---|---|
daily_spend_cap_cents | Max rated spend per day. Resets on your tenant's daily boundary. Over it → 403 daily_spend_cap_exceeded. |
monthly_spend_cap_cents | Max rated spend per calendar month. |
concurrent_call_limit | Max simultaneous calls. Over it → 403 concurrent_call_cap_exceeded. |
max_destination_rate_cents | Per-minute rate ceiling for a destination. Pricier routes are refused before dialing. |
allowed_country_codes | If non-empty, only these ISO-3166 country codes may be dialed. Empty = no allow-list. |
blocked_country_codes | Destinations in these countries are refused (403 country_blocked). |
blocked_prefixes | Number prefixes on this list are refused (403 prefix_blocked). |
allow_caller_id_forwarding | When true, POST /v1/calls may set a from you don't own. Admin-opt-in after a STIR/SHAKEN review. See Calls. |
Live usage
| Field | Meaning |
|---|---|
daily_spent_cents | Rated voice spend so far today, as a positive number. Compare to daily_spend_cap_cents. |
monthly_spent_cents | Rated spend so far this month, positive. Compare to monthly_spend_cap_cents. |
in_flight_calls | Calls currently non-terminal (initiated, ringing, answered, bridged). Compare to concurrent_call_limit. |
balance_cents | Live credit, mirrored here so a single call tells you both "can I afford it" and "am I within caps". |
credit_limit_cents | Same as on the account balance. |
tenant_status | pending_verification | active | suspended | closed. |
as_of | When this snapshot was computed (UTC). |
daily_spent_cents and monthly_spent_cents are derived from your
usage ledger entries (the usage kind below). Poll this endpoint to
back a spend gauge or to throttle your own dialer before you hit a
hard cap.
Cap changes are admin-only. If you need a higher ceiling, contact support — you can't raise your own caps through the API.
Usage ledger
Every movement of money — top-ups, per-call charges, monthly number fees, activation fees, refunds, and manual adjustments — is one append-only row in your ledger. The ledger is the authoritative record of what you were charged and when; nothing is ever edited or deleted, only appended.
GET /v1/usage
{
"count": 842,
"next": "https://api.voepy.com/v1/usage?page=2",
"previous": null,
"results": [
{
"id": "…",
"kind": "usage",
"amount_cents": -12,
"running_balance_cents": 12438,
"reference_type": "call",
"reference_id": "voepy_call_01HZABC…",
"description": "Outbound call to +15558675309 (38s)",
"metadata": { "duration_seconds": 38, "destination": "US" },
"created_at": "2026-05-12T14:33:01.000Z"
},
{
"id": "…",
"kind": "topup",
"amount_cents": 5000,
"running_balance_cents": 12450,
"reference_type": "payment",
"reference_id": "pi_…",
"description": "Card top-up",
"metadata": {},
"created_at": "2026-05-12T14:00:00.000Z"
}
]
}
Entry fields
| Field | Meaning |
|---|---|
id | Opaque entry id. |
kind | What kind of movement (see table below). |
amount_cents | Signed. Positive = credit (money added). Negative = debit (money spent). |
running_balance_cents | The balance after this entry was applied — your audit trail of the balance over time. |
reference_type | The kind of thing that caused the entry, e.g. call, payment, phone_number. May be blank. |
reference_id | The id of that thing — cross-reference to a Call, payment, etc. May be blank. |
description | Human-readable summary. |
metadata | Free-form JSON with entry-specific detail (duration, destination, …). |
created_at | When the entry was written. |
Sign convention
amount_cents carries the sign: credits are positive, debits are
negative. A $50 top-up is +5000; a 12¢ call charge is -12. Sum
the column over a window and you get the net change to your balance;
running_balance_cents lets you reconcile any single row against the
balance at that instant. (Note the live spend figures in
GET /v1/quota flip the sign on usage debits so they read as
positive spend — same numbers, opposite sign.)
Entry kinds
kind | What it is | Typical sign |
|---|---|---|
topup | Customer payment (card top-up). | + |
usage | Per-call (and per-minute feature) voice charges. | − |
nrc | Non-recurring charge — one-off activation fee (new DID, lookup, etc.). | − |
mrc | Monthly recurring charge — a number's monthly fee. | − |
refund | Money returned to your balance. | + |
adjustment | Manual correction by an admin. | ± |
These map directly to how you're billed — see Billing for what drives each charge.
Filtering & pagination
GET /v1/usage?kind=usage&page=2&page_size=100
| Param | Default | Notes |
|---|---|---|
kind | — | Filter to one kind: topup, usage, nrc, mrc, refund, adjustment. |
page | 1 | 1-indexed page number. |
page_size | 20 | Max 200. |
Entries come back newest-first. The envelope is page-number based:
count is the total across all pages, next/previous are full URLs
(or null at the ends), and results is the page. Walk pages by
following next until it's null.
Audit log
The audit log records who changed your account configuration — membership, roles, API keys, quota, profiles, and the tenant itself. It exists for security and compliance: a tamper-evident, append-only trail of administrative actions.
GET /v1/audit
{
"count": 137,
"next": "https://api.voepy.com/v1/audit?page=2",
"previous": null,
"results": [
{
"id": "…",
"action": "api_key.issued",
"target_type": "api_key",
"target_id": "…",
"actor_email": "ops@yourco.com",
"api_key_prefix": null,
"before": null,
"after": { "name": "CI deploy key", "scopes": ["calls:write"] },
"ip": "203.0.113.7",
"user_agent": "Mozilla/5.0 …",
"created_at": "2026-05-12T14:32:18.000Z"
},
{
"id": "…",
"action": "member.role_changed",
"target_type": "membership",
"target_id": "…",
"actor_email": "owner@yourco.com",
"api_key_prefix": null,
"before": { "role_slug": "member" },
"after": { "role_slug": "admin" },
"ip": "198.51.100.4",
"user_agent": "Mozilla/5.0 …",
"created_at": "2026-05-11T18:02:44.000Z"
}
]
}
Entry fields
| Field | Meaning |
|---|---|
id | Opaque entry id. |
action | What happened, as a dotted constant (see below). |
target_type | The kind of resource acted on. |
target_id | That resource's id (may be blank). |
actor_email | The user who performed the action, or null when the actor was an API key. |
api_key_prefix | The key prefix that performed the action, or null when a user did it. |
before | Snapshot of the changed fields before the action (null on create). |
after | Snapshot after (null on delete). |
ip | Source IP, or null if not captured. |
user_agent | Client user-agent string. |
created_at | When the action happened. |
actor_email and api_key_prefix are mutually exclusive: exactly one
identifies the actor, the other is null. The before/after pair
gives you a field-level diff for any change.
Recorded actions
action values are dotted resource.verb constants. The full set
currently emitted:
target_type | action values |
|---|---|
tenant | tenant.signup, tenant.updated, tenant.created_by_admin, tenant.activated_via_verification, tenant.suspended_by_admin, tenant.suspended_unverified, tenant.auto_suspended, tenant.resumed_by_admin |
membership | member.invited, member.removed, member.role_changed |
role | role.created, role.updated, role.deleted |
api_key | api_key.issued, api_key.revoked |
quota | quota.updated_by_admin |
ledger_entry | ledger.adjustment_by_admin |
tenant (profiles) | inbound_profile.updated, outbound_profile.updated |
See Team & roles for the membership and role actions in context.
Pagination
GET /v1/audit?page=2&page_size=50
Same page-number envelope as the usage ledger: page (default 1)
and page_size (default 20, max 200), newest-first, with
count/next/previous/results. The audit endpoint does not take
field filters — page through and filter client-side, or narrow by
following next.
Access is gated by the audit:read scope; issue keys (or assign
roles) accordingly so only trusted operators can read the log.
Reconciling usage vs billing
The usage ledger and your Stripe billing answer different questions, and it's normal for them not to line up one-to-one:
- The ledger is your prepaid balance's history. Top-ups, per-call
usagecharges,nrc/mrcfees, refunds, and adjustments all hit it.running_balance_centson the newest row equalsbalance.balance_centsfromGET /v1/account. - Stripe invoices cover MRC + overage minutes only. Top-ups and per-call rated charges are already debited from your balance, so they're not re-billed on the monthly invoice.
To verify a period: sum amount_cents for kind=usage over the
window from GET /v1/usage — that's your rated voice spend — and it
should match monthly_spent_cents from GET /v1/quota for the
current month. To tie a single call's charge back to the call, match
the ledger entry's reference_id to the Call.id. See
Billing for how invoices and the balance relate.
Next
- Billing — fund the balance these endpoints report on.
- Team & roles — manage the members and keys the audit log tracks, and the scopes that gate these reads.
- Error reference — the
402/403codes the quota gates return.
