GET /v0/usage/ledger returns product quantities, estimated and provider-confirmed
USD costs, outstanding reservations, unpriced activity and the coverage start.
GET /v0/usage/events exposes recent individual records.
Coverage and cost provenance
Accounting starts when migration0023_usage_budgets.sql is applied. Historical
messages are never reconstructed as authoritative charges. Email delivery is
counted per recipient; inbound email/SMS per message and outbound SMS per complete
send (not per segment); proxy allocation in
MB; browser/container/call runtime in seconds. Runtime observations can be upper
bounds when the provider’s exact termination time is unavailable.
Every amount is integer microUSD (1 USD = 1,000,000 micros). Activity without a
verified unit rate remains unpriced, not zero cost. No price is hardcoded.
Configure the operator-only USAGE_VERIFIED_RATES_JSON binding with verified
maximum unit charges from the appropriate provider contract:
sms.sent, the configuration additionally requires
rate_basis: "max_total_send_cost_including_segments_media_and_all_destinations".
Its unit_cost_micros must bound the entire send: the maximum accepted
1,600-character input including Unicode, all resulting segments, up to ten media
URLs, carrier fees and every destination the workspace can send to. A per-segment
or base-message price is insufficient. Missing or different rate_basis keeps
SMS activity unpriced and blocks new sends under a matching spending limit.
Lupin does not guess segment counts, destination rates or media surcharges. Configured amounts are labeled configured_estimate.
Signed Telnyx message.finalized callbacks can replace SMS estimates with the
provider’s reported USD cost.amount, labeled provider_confirmed. Other
currencies, absent amounts and unsupported precision remain unpriced; no
exchange rate or missing fee is invented. Subscription invoices and recurring
provider fees are separate, so the summary explicitly remains partial.
Limits and alerts
POST /v0/budgets accepts scope_type (workspace, project, api_key), a
scope_id for project/key scopes and positive integer limit_micros.
GET /v0/budgets lists spent/reserved amounts, unknown activity and durable
80%/100% threshold alerts. DELETE /v0/budgets/:budget_id disables a limit;
existing history remains. Management requires a workspace key with
api_key_create. Financial reads require api_key_read; project-scoped keys
only see their project and cannot change budgets. Inbox keys are rejected.
Limits reset by UTC calendar month. Outstanding reservations from earlier
months continue counting until resolved. Unattributed activity conservatively
counts toward matching project/key limits, including scheduled work whose
original agent identity is unavailable. This prevents missing attribution from
bypassing an agent’s limit.
Admission checking and reservation insertion happen in one serialized SQL
statement, so concurrent isolates cannot both spend the same remaining budget.
A definitive provider rejection releases the reservation. Timeouts and unknown
outcomes keep it until provider reconciliation; retries never silently issue
another paid action. Known provider acceptance can recover the local resource
projection without repeating the external operation. Browser creation requires
an Idempotency-Key, as do the other journaled paid resource mutations.
An active matching limit blocks unpriced paid actions. Browser/call/container
runtime and number recurring charges currently have no enforceable maximum
charge in Lupin’s adapters, so new allocations/resumes are blocked under a cap.
A limit cannot be enabled around outstanding unpriced or unbounded activity.
These limits gate new actions using verified estimates; they do not promise
that an invoice cannot exceed the cap. Existing running resources, inbound
traffic and recurring provider charges are not automatically stopped. Reads,
known-result recovery and cleanup/hangup/stop actions remain available.
Signed phone callbacks
SetTELNYX_PUBLIC_KEY to the account’s base64 Ed25519 webhook signing key.
Without this binding, Lupin retrieves the account key over authenticated
GET https://api.telnyx.com/v2/public_key using TELNYX_API_KEY, cached for five
minutes. Missing/unavailable verification returns 503; missing, stale or invalid
signatures return 401 before processing. The signature covers the exact
timestamp|raw_body bytes and accepts a five-minute timestamp window. See
Telnyx webhook signing.