Messaging
Usage records
Reconcile Developer Platform activity with estimates, finalized charges, and zero-cost Test Mode records.
What this guide covers
- Join usage records to messages with public IDs.
- Distinguish reserved, released, finalized, and reconciliation-needed usage.
- Keep USD amounts as decimal strings until they reach a decimal-safe money type.
List usage records
GET /v1/usage requires usage:read. It returns Developer Platform usage records restricted to the key's Yanola Account, Agent Project, environment, and allowed Senders.
Usage records show what each accepted message cost in Yanola fees. A Live Mode API or SDK send is included in the Plan, so its record is finalized at 0.000000 and spends no Yanola Balance. Usage records are not the current Balance ledger or subscription entitlement state.
import { WhatsAppClient } from "@yanola/whatsapp"
const apiKey = process.env.YANOLA_API_KEY
if (!apiKey) {
throw new Error("Missing YANOLA_API_KEY")
}
const yanola = new WhatsAppClient({ apiKey })
const usage = await yanola.usage.list({
status: "finalized",
from: "+15557654321",
createdAfter: "2026-07-01T00:00:00.000Z",
createdBefore: "2026-08-01T00:00:00.000Z",
limit: 100,
})
for (const record of usage.data) {
console.log(record.messageId, record.totalAmount)
}Usage lifecycle
| Status | Meaning | totalAmount |
|---|---|---|
reserved | An amount is held while the final charge is unresolved. Included API and SDK sends never use this status. | 0.00 until finalized |
released | The reservation was released without a finalized charge. | 0.00 |
finalized | The record is settled. Included API and SDK sends are finalized when accepted. | 0.000000 on an included send |
needs_reconciliation | Yanola could not safely determine the final charge yet. | 0.00 until reconciled |
Synthetic Test Mode sends create zero-amount usage records but do not reserve or consume Yanola Balance and do not call Meta.
Amounts are decimal USD strings
All monetary values are strings so JavaScript does not introduce binary floating-point rounding. currency is always USD in the v1 contract.
function toMicros(amount: string): bigint {
const [whole, fraction = ""] = amount.split(".")
return BigInt(whole) * 1_000_000n +
BigInt(fraction.padEnd(6, "0"))
}
const totalMicros = usage.data.reduce(
(sum, record) => sum + toMicros(record.totalAmount),
0n
)| Field | Meaning |
|---|---|
estimatedAmount | Amount estimated or reserved when the message was accepted. |
providerAmount | null on an included send: Meta bills your WhatsApp Business Account directly and Yanola charges no Meta Fee. |
yanolaFee | null on an included send: API and SDK sends carry no Yanola fee. |
totalAmount | Amount deducted from Yanola Balance; 0.000000 on an included send, and zero for reserved, released, and needs_reconciliation records. |
Correlate pricing with a message
| Field | Meaning |
|---|---|
id | Opaque usg_ usage record ID. |
reservationId | Optional resv_ identity for the original Live Mode reservation. |
messageId | Related msg_ or msg_test_ public message ID when available. |
from | E.164 Sender associated with the activity. |
providerCategory | Provider message category captured in the pricing record. |
market | Provider pricing market captured by the latest snapshot. |
createdAt / finalizedAt | Creation time and optional finalization time. |
Filter and paginate
Filter by from, status, category, createdAfter, or createdBefore. REST uses created_after and created_before; the SDK maps the camel-case parameters for you.
Usage lists default to 20 and accept at most 100. Continue with pagination.nextCursor while pagination.hasMore is true. Cursors are opaque and bound to the key, environment, and filters.
Interpret usage safely
- Expect totalAmount 0.000000 on included API and SDK sends; they count against the Message Cap, not the Balance.
- Keep reserved and needs_reconciliation records visible in operational reports instead of treating them as free activity.
- Use messageId to join a usage record to message state; do not infer delivery from usage status.
- Use Balance and billing views for current funds, top-ups, invoices, and adjustments.
Usage errors
| Status and code | Meaning | Action |
|---|---|---|
400 invalid_usage_list_query | A filter, time range, limit, or cursor is invalid. | Correct the query or restart pagination. |
400 environment_mismatch | The query environment differs from the key. | Use the key's environment. |
403 insufficient_scope | The key lacks usage:read. | Use a key with usage access. |
429 rate_limit_exceeded | The current read window is exhausted. | Honor Retry-After and reduce overlapping scans. |