> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getdialed.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Quota and progress

> Read how much provider budget is left, how deep a Domain's queue is and when it should drain, what an upload will cost before you commit to it, and which credential is spending the budget

How much budget do I have left, and when will my upload finish? Both are readable, and both come with a caveat you should read before you build on them: every number on this page is a **live projection** of the quota ledger, computed the moment you ask. None of it reserves anything. The four surfaces below exist so you can *show* a customer what is happening and *explain* why work is pacing — not so a client can decide whether a call is allowed to go out. That decision is made server-side, at the moment of the call, and never anywhere else.

Everything here is scoped to one [Domain](/concepts/platform-tenancy) and readable by any API key in your organization. A Domain belonging to another organization returns `404`, never `403`.

## Remaining budget

```
GET /platforms/five9/domains/{tenancy_id}/headroom
```

One request returns every [rate bucket](/concepts/platform-tenancy#rate-buckets-and-effective-limits) and all three of its windows — 60 seconds, one hour, one day:

```json theme={null}
{
  "tenancy_id": "ten_9f2c1a7b3d4e5f60",
  "as_of": "2026-08-14T18:00:00Z",
  "buckets": {
    "Upload": {
      "label": "Uploading (batch)",
      "windows": {
        "60": { "used": 4, "cap": 20, "remaining": 16 },
        "3600": { "used": 120, "cap": 400, "remaining": 280 },
        "86400": { "used": 900, "cap": 2000, "remaining": 1100 }
      },
      "escalated": false,
      "next_allowed_at": null
    }
  }
}
```

`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.

<Warning>
  **Do not read this and then decide whether to send.** A `remaining` value is a projection, not a reservation: nothing is set aside for you, every credential resolving to this Domain draws on the same allowance, and systems outside GetDialed can spend the same Domain's provider budget without going through us at all. The value can change between your read and your call. The platform's own dispatcher performs an atomic check at the moment of the call and is the only arbiter of whether that call may proceed — so a client-side budget check adds nothing except a race and the over-dialling that comes with losing it. Submit the work; let the dispatcher pace it.
</Warning>

### How closely it agrees with the provider

GetDialed periodically reconciles the ledger against Five9's own `getCallCountersState` 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](#per-credential-attribution) as the unattributed remainder.

## Queue depth and estimated completion

```
GET /platforms/five9/domains/{tenancy_id}/queue
```

Depth is reported in **three units**, because a single "how much is waiting" number would conflate three different things:

| Unit              | What it counts                                             | Exact?       |
| ----------------- | ---------------------------------------------------------- | ------------ |
| `queued_records`  | Rows waiting to be delivered.                              | Exact        |
| `queued_items`    | Queued chunk references — the units the dispatcher claims. | Exact        |
| `estimated_calls` | Projected provider calls those items will cost.            | **Estimate** |

`estimated_calls` is labelled an estimate on purpose. Work aimed at the same destination [merges into fuller provider calls](/concepts/dispatch-pacing#how-queued-work-is-merged) 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

The `completion` object does not hand you a bare timestamp. It says *what constrains it*:

```json theme={null}
{
  "eta_seconds": 3600.0,
  "eta_at": "2026-08-14T19:00:00Z",
  "binding_window": "3600",
  "binding_source": "window",
  "unknown_reason": null,
  "hold_causes": [],
  "caveats": ["estimated_calls_pre_coalescing_projection"],
  "paced_batches_active": 0
}
```

`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:

| `binding_source` | Meaning                                                                                                                                                        |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `window`         | A rate window binds — the queue drains at the bucket's permitted rate. `binding_window` names which one.                                                       |
| `spacing`        | The minimum interval enforced between consecutive calls binds before any window does.                                                                          |
| `pacing`         | A [paced-release policy](/concepts/dispatch-pacing#paced-release) on one or more batches is slower than the provider rate. `paced_batches_active` counts them. |
| `hold`           | A pause with a known expiry is the constraint. `hold_causes` names the holds folded into the figure.                                                           |
| `empty`          | Nothing is queued, so there is nothing to wait for.                                                                                                            |

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:

| `unknown_reason`            | What happened                                                                            | What to do                                                                                                                                                                                                                                      |
| --------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paused_indefinitely`       | A hold with no scheduled resume is in force, so nothing can say when the queue restarts. | List what is holding the Domain with `GET /platforms/five9/domains/{tenancy_id}/pauses`, then resume it — or give it a `duration_seconds` so it has an expiry the estimate can use. See [pause scopes](/concepts/dispatch-pacing#pause-scopes). |
| `escalated_near_exhaustion` | The bucket is backing off near exhaustion, so its drain rate is no longer predictable.   | Nothing is broken and nothing is lost. Check `escalated` on [headroom](#remaining-budget) and wait for the window to roll over; the estimate returns to a number on its own.                                                                    |
| `no_effective_cap`          | The bucket has no usable budget to divide by, so there is no rate to project from.       | Check the Domain's `sync_status` and margin, and re-read the provider's limits with `POST /platforms/five9/domains/{tenancy_id}/refresh-limits`. A margin cannot be `0`, so this normally means no usable cap has ever landed for that bucket.  |

<Note>
  **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](/concepts/dispatch-pacing#paced-release) 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.

  You may also see `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.
</Note>

Completion here is **Domain-level** — when this Domain's queue should drain. For "how far along is *my* upload", read the batch's own per-row counters; [import outcomes](/concepts/import-outcomes) is the model for what each row's answer means.

## Pre-flight cost estimate

```
POST /platforms/five9/domains/{tenancy_id}/estimate
```

Ask what an upload would cost **before** committing to it. This endpoint commits nothing: no records are ingested, no batch is created, no budget is reserved, nothing is written.

Give exactly **one** row source — `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.

```bash theme={null}
curl -X POST "$BASE_URL/platforms/five9/domains/ten_9f2c1a7b3d4e5f60/estimate" \
  -H "X-API-Key: $GETDIALED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "bucket": "Upload",
    "action_id": "five9__configuration_service__add_to_list",
    "record_count": 1000000
  }'
```

```json theme={null}
{
  "bucket": "Upload",
  "records": 1000000,
  "calls": 20,
  "max_records_per_call": 50000,
  "day_remaining_calls": 1100,
  "pct_of_remaining_day": 1.8,
  "queued_estimated_calls": 0,
  "spills_past_day_cap": false,
  "warning": null,
  "as_of": "2026-08-14T18:00:00Z"
}
```

Records become calls at the action's `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.

<Note>
  **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.
</Note>

`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](/guides/bulk-ingestion#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:

```json theme={null}
{
  "batch_id": "batch_55667788",
  "definition_id": "def_a1b2c3d4",
  "status": "pending",
  "deduplicated": false,
  "estimate": {
    "bucket": "Upload",
    "records": 1000000,
    "calls": 20,
    "max_records_per_call": 50000,
    "day_remaining_calls": 1100,
    "pct_of_remaining_day": 1.8,
    "queued_estimated_calls": 0,
    "spills_past_day_cap": false,
    "warning": null,
    "as_of": "2026-08-14T18:00:00Z"
  }
}
```

`estimate` is `null` when the flow does not dispatch through a rate-limited platform budget — there is nothing to price.

<Warning>
  **The inline `estimate` appears on the manual trigger response only.** A batch started by a [schedule](/concepts/schedules), a [webhook](/concepts/webhooks) or an [event subscription](/concepts/event-subscriptions) has no HTTP response for you to read — nobody is waiting on the other end of the call — so it structurally cannot carry one. To know the cost of automated work ahead of time, call `POST /platforms/five9/domains/{tenancy_id}/estimate` yourself with the row count you expect; the figures are identical because it is the same estimator behind both surfaces.
</Warning>

## Per-credential attribution

```
GET /platforms/five9/domains/{tenancy_id}/attribution
```

Several [credentials](/concepts/credentials) can resolve to the same Domain, and they all draw on **one** shared provider budget. This surface answers "which credential lane is consuming it?", broken down per credential for every bucket and all three windows:

```json theme={null}
{
  "used": 900,
  "cap": 2000,
  "entries": [
    { "credential_id": "cred_1a2b3c4d", "credential_name": "Acme Outbound", "used": 700, "pct_of_used": 77.8 },
    { "credential_id": null, "credential_name": null, "used": 200, "pct_of_used": 22.2 }
  ]
}
```

### The unattributed remainder

Every window carries one entry whose `credential_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.

<Note>
  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.
</Note>

## Usage history

```
GET /platforms/five9/domains/{tenancy_id}/usage
```

The recorded rate-limit usage series for a Domain, newest first. Each row is one downsampled observation for a bucket and window:

```json theme={null}
{
  "bucket": "Upload",
  "window": "3600",
  "minute": "2026-08-14T18:00:00Z",
  "used": 120,
  "cap": 400,
  "observed": 118,
  "drift": -2
}
```

`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 `bucket` and `window` (both optional and additive), then pass the previous response's `next_cursor` back verbatim in `cursor`. The cursor is opaque: do not parse it. `next_cursor: null` means you have reached the oldest retained row, and `total` is `null` on this feed by design — counting a 30-day series on every page would cost more than the page itself.

## Next steps

<CardGroup cols={2}>
  <Card title="Dispatch pacing" icon="gauge-high" href="/concepts/dispatch-pacing">
    Why work waits: merging, lane order, paced release, and what a pause holds.
  </Card>

  <Card title="Platform tenancy" icon="building" href="/concepts/platform-tenancy">
    The Domain record whose rate buckets and safety margins set every budget on this page.
  </Card>

  <Card title="Bulk ingestion" icon="upload" href="/guides/bulk-ingestion">
    Stage millions of records, trigger by reference, and read the inline estimate.
  </Card>

  <Card title="Import outcomes" icon="list-check" href="/concepts/import-outcomes">
    Per-row progress for one batch, which is what "how far along is my upload" really means.
  </Card>
</CardGroup>
