404, never 403.
Remaining budget
cap is the effective budget — the provider’s cap after whatever safety margin you have applied to that bucket — so it can read lower than the limit Five9 publishes. remaining is cap minus what the ledger records as spent. escalated means the bucket is close enough to exhaustion that it has started checking the provider’s own counters before each call, and next_allowed_at is the earliest moment the next call is expected to be permitted, or null when nothing is holding it. as_of is when the counters were read, and is null for a Domain that has never dispatched.
How closely it agrees with the provider
GetDialed periodically reconciles the ledger against Five9’s owngetCallCountersState counters. The published reconciliation tolerance is 20% of that window’s effective cap: within a window, the used figure on this surface and the provider’s own counter for the same bucket agree to within 20% of cap. On an Upload bucket at Five9’s published caps with an 80% margin — an effective 16 per minute, 320 per hour, 1,600 per day — that is 3 calls in the minute window, 64 in the hour, and 320 in the day.
Two structural reasons the two figures are not always identical, both visible rather than hidden:
- Other callers. Anything else spending the same Domain’s Five9 budget is counted by Five9 and not by us. When the provider reports higher than the ledger, reconciliation clamps the ledger up to the provider’s number — the provider sees callers we cannot, and the safe direction is always to believe the larger figure.
- Calls made outside the paced dispatch path. A handful of GetDialed’s own synchronous calls — testing a credential, refreshing a Domain’s limits — spend real budget without being attributed to a credential. They show up in attribution as the unattributed remainder.
Queue depth and estimated completion
estimated_calls is labelled an estimate on purpose. Work aimed at the same destination merges into fuller provider calls when it is claimed, so the real call count is usually lower and can only be known at dispatch time. Every completion estimate computed from a non-empty queue carries the machine-readable caveat estimated_calls_pre_coalescing_projection for exactly that reason.
Each unit also reports the share currently held by a pause — paused_records, paused_items — and estimated_calls_unpaused is the projected cost of the work that is actually free to run. That last figure is what the completion estimate paces against.
The estimate names what is holding your work
Thecompletion object does not hand you a bare timestamp. It says what constrains it:
binding_window is the window whose limit binds — "60", "3600" or "86400" — so you can tell a customer whether it is the per-minute, the hourly or the daily limit holding their work. A window becomes a candidate for binding when the queued work exceeds what is left in that window — its remaining, not its full cap. One window is always a candidate regardless: the tightest one, because its permitted rate is the spacing floor between consecutive calls. Without that rule the daily window would appear to bind every projection on a Five9 Domain and report half an hour for work that finishes in two minutes.
Judging candidacy on remaining matters most on a busy Domain. A window that is mostly spent binds work that would comfortably have fitted its cap — 100 queued calls against an hour with 40 of its 320 left spend those 40 now, and the rest cannot go until the window rolls — so the estimate includes the wait for that roll. It reads later than the naive figure you would get from the cap alone, which is the only safe direction for a number a customer plans around: this surface will tell you later, never earlier.
binding_source names the category of constraint:
Holds and the spacing floor add to the drain rather than replacing it: the queue still has to drain after a hold lifts, so an estimate that substituted the hold’s expiry for the drain would under-report. Estimates are computed fresh on every read, so nothing here can go stale between requests.
Why completion can be unknown
When completion is genuinely indeterminate,eta_seconds and eta_at read null and unknown_reason always names the cause. unknown is an honest answer, not a failure — a fabricated number that a customer plans around is strictly worse than admitting the drain rate cannot be projected. There are exactly three reasons, and each has something you can do about it:
What this estimate cannot see. Two limits are disclosed as machine-readable tokens in
caveats rather than left for you to discover:unreleased_paced_rows_not_visible— rows a paced batch has not released yet sit upstream of the queue, so they are structurally invisible to this read. The pacing term covers what is bound but not yet released; the rest is not counted.legacy_batch_counters_missing— a batch minted before per-row rollup counters existed contributes no pacing term, so a Domain still draining one of those reads slightly optimistic.
batch_sample_truncated: the pacing term samples a bounded number of batches, so a pathological queue cannot turn one read into an unbounded series of lookups.Pre-flight cost estimate
record_set_id for rows you have already uploaded, or record_count for a hypothetical size — and at least one of bucket or action_id so the estimate knows which provider budget to price against. Supplying both row sources, or neither, is a 422: two sources could disagree and there is no principled way to pick. An action this Domain does not meter, or one that spends more than one of its budgets, is rejected rather than priced against a guess.
max_records_per_call — a million rows is twenty upload calls at 50,000 each — and pct_of_remaining_day is what that costs as a share of today’s remaining budget.
The percentage and the spill verdict answer different questions.
pct_of_remaining_day is this upload’s own cost. spills_past_day_cap also counts queued_estimated_calls — the work already queued ahead of you. That is why a modest upload can read as a small percentage and still be flagged as spilling: the backlog in front of it already consumed the day. Without that, two uploads that individually fit but jointly overflow would both read clean.day_remaining_calls, pct_of_remaining_day and spills_past_day_cap all read null when the remaining daily budget cannot be read. A share of an unknown budget is unknown, not zero.
A spill warning never blocks a trigger
spills_past_day_cap: true does not mean the work would be refused. It means the work needs more provider calls than today’s remaining budget, so the overflow is delivered at your Domain’s permitted rate and continues into the following day. Multi-day pacing is the product working as designed, not a failure mode — and warning carries customer-ready copy saying so.
If you change your mind, the brake is cancelling the batch, not a refused trigger: POST /flows/batches/{batch_id}/cancel stops all future sends and settles everything not yet sent as unprocessed. See stopping a bad upload.
The same estimate, inline on a trigger
A manual trigger —POST /flows/definitions/{definition_id}/trigger — returns the same object inline as estimate, so you get the cost of the work you just committed to without a second call:
estimate is null when the flow does not dispatch through a rate-limited platform budget — there is nothing to price.
Per-credential attribution
The unattributed remainder
Every window carries one entry whosecredential_id is null. That is the unattributed remainder, and it is expected rather than an error. Because it is always present, pct_of_used totals 100 within every window — spend nobody can attribute stays visible instead of being quietly spread across your credentials and making the denominator a lie.
Two things land there, and a non-zero remainder is normal for both reasons:
- Other systems spending the same Domain’s budget. Anything outside GetDialed that calls Five9 against this Domain is counted by the provider, and reconciliation clamps our ledger up to the provider’s own counter when it reports higher. That difference cannot belong to any of your credentials, because none of them made those calls.
- GetDialed’s own synchronous calls. A small number of calls made outside the paced dispatch path — testing a credential, for instance — are not attributed today.
Display names are resolved when you read, so a renamed credential is never stale. A credential you have since deleted keeps its row with a
credential_name of null: the calls were really made, and dropping the row would move real spend out of the total. An entry can also name a credential that was retired before the current model — the row keeps its recorded spend and reads with a null name, because a retired identifier is not a display name.Usage history
used is what GetDialed recorded spending and cap the effective budget at the time. observed is the provider’s own counter when a reconciliation read happened to run in that minute, and drift the difference between the two — both are null in minutes with no reconciliation read. A sustained non-zero drift means something outside GetDialed is also spending the Domain’s budget.
Three properties worth knowing before you build a chart on it:
- Counts only. The series never contains a record value, a phone number or a list name. That is what makes it safe to read and export in bulk.
- Retained 30 days, on a rolling window. The history is bounded rather than complete — older observations age out and are gone.
- Cursor-paged. Filter with
bucketandwindow(both optional and additive), then pass the previous response’snext_cursorback verbatim incursor. The cursor is opaque: do not parse it.next_cursor: nullmeans you have reached the oldest retained row, andtotalisnullon this feed by design — counting a 30-day series on every page would cost more than the page itself.
Next steps
Dispatch pacing
Why work waits: merging, lane order, paced release, and what a pause holds.
Platform tenancy
The Domain record whose rate buckets and safety margins set every budget on this page.
Bulk ingestion
Stage millions of records, trigger by reference, and read the inline estimate.
Import outcomes
Per-row progress for one batch, which is what “how far along is my upload” really means.