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

# Credits and limits

> What a verification costs, what stops when a workflow runs out, and how to read where one stands.

Every workflow is sold a number of prepaid credits, and one verification uses one credit. A workflow
with none left starts no new verifications and moves none of the ones it already has.

Credits are held **per workflow**, not per account. A workflow you bought 500 runs of has its own
balance, and the runs on that workflow are the only thing that draws it down. Nothing you spend on one
journey comes out of another.

## What uses a credit

One run of one workflow uses exactly one credit, whatever the workflow asks for and however many of its
steps ran. A verification that walked twelve steps and one that walked three cost the same.

The credit is used when the verification **ends**, not when you create it:

| The verification reaches | Credit |
| - | - |
| `completed` | One. The run finished. |
| `cancelled` | One. You ended a run that had already started. |
| `expired` | One, where it had not already ended. An abandoned journey used the run as much as a finished one. |
| `gated` | None. A check of yours stopped the journey, so the run ended at your own gate. |
| `failed` | None. The run could not be carried through, so you are not charged for it. |
| `open`, `submitted` | None yet. Nothing has ended. |

One run uses one credit for one ending, never two. A record that completed and then expires when its
retention window closes used one credit in total; a record marked `failed` or `gated` that later
expires used none.

### A reset is a run of its own

Resetting a verification runs the subject through again, so the run it opens uses a credit of its own
when that run ends. `credits_consumed` on the record counts every run of it, not the record:

```json theme={"system"}
{
  "id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
  "status": "completed",
  "credits_consumed": 2
}
```

A verification created, completed, reset and completed again reads `2`. One still open reads `0`.

Resetting a verification that never ended adds nothing to `credits_consumed`: the run you cleared was
never charged for, and the one you opened is charged when it ends. Nobody having paid for that run is
exactly what makes it draw on the workflow's reset allowance instead. See
[When the resets run out](#when-the-resets-run-out).

## Reading where a workflow stands

`GET /workflows` and `GET /workflows/{workflow_key}` both report `credits`, `runs` and `resets`:

```json theme={"system"}
{
  "key": "merchant-onboarding",
  "mode": "hosted",
  "environment": "production",
  "credits": {
    "granted": 700,
    "consumed": 412,
    "expired": 0,
    "revoked": 0,
    "remaining": 288,
    "expiring": [
      { "amount": 88, "on": "2026-09-30" },
      { "amount": 200, "on": "2027-03-31" }
    ]
  },
  "runs": { "live": 17, "limit": 25 },
  "resets": {
    "granted": 50,
    "drawn": 6,
    "expired": 0,
    "revoked": 0,
    "remaining": 44,
    "expiring": [{ "amount": 24, "on": "2027-03-31" }, { "amount": 20, "on": "2027-06-30" }]
  }
}
```

| Field | Means |
| - | - |
| `credits.granted` | Every credit granted to this workflow, each top-up included. Where they went afterwards is what the rows below say, so this only ever rises. |
| `credits.consumed` | Credits the runs on it have used. |
| `credits.expired` | Credits that ran out of time before anything spent them. |
| `credits.revoked` | Credits we have taken back off it, which is a correction we make and tell you about. |
| `credits.remaining` | Credits left to spend. |
| `credits.expiring` | What the remaining credits are standing on, soonest to expire first. Adds up to `remaining` wherever there is anything left to spend, and empty when there is not. |
| `runs.live` | Verifications on it that have not ended: every one reading `open` or `submitted`. |
| `runs.limit` | How many it may have going at once. |
| `resets.granted` | Resets sold to this workflow in total. They come with credits, on the same terms. |
| `resets.drawn` | Resets its verifications have drawn between them, counted over every verification it has ever had. |
| `resets.expired` | Resets forfeited when their term passed, alongside the credits they were sold with. |
| `resets.revoked` | Resets we have taken back. |
| `resets.remaining` | Resets left. At zero, resetting an `open` or `submitted` verification is refused with `402`. |
| `resets.expiring` | What the remaining resets are standing on, soonest to expire first. |

The three figures that take credits away account for the whole of the difference, so `granted` less
`consumed`, `expired` and `revoked` is always `remaining`. Reading a workflow never uses a credit, so
poll it as often as you find useful.

### Credits are granted on a term

Every credit is granted on a term. Each time a workflow is topped up, the credits come with an `on`
date of their own: the last day they can be spent on. They are good for all of that day, and once it has passed whatever was still standing
on that term is gone. `expired` rises by that much and `remaining` falls by the same, on that date.

What a term forfeits is what it was holding on the day it passed. Runs you make afterwards come out of
the credits that are still live, never out of the ones that expired, so what a term took is settled on
its last day and does not change afterwards.

Spending draws on the term that comes up soonest, so credits are used in the order they would otherwise
be lost. Topping up never moves the date on the credits you already had: a top-up bought today carries
its own term and leaves every earlier one exactly where it was.

`expiring` is what to read. The workflow above holds 288 credits, but they are not one pool with one
date on it: 88 of them go at the end of September and the other 200 run to the end of March. A single
date could only have told you one of those, so the breakdown is what the API reports and the entries
are ordered by the one that matters first.

Credits that expire are the one thing that moves `remaining` with no verification behind it. Where
you find a balance lower than you expected and `consumed` has not moved, `expired` is the figure that
explains it.

## Two balances and a ceiling, and what each one stops

The balance, the live ceiling and the reset allowance answer different questions. The balance caps the
work that can **finish**; the live ceiling caps the work you can have **in flight**; the allowance caps
the work you can have **re-run without paying for it**. A verification that has not ended has
used no credit, so the three count different things and any of them can be the one in your way.

| | Balance | Live ceiling | Reset allowance |
| - | - | - | - |
| Read it as | `credits.remaining`, broken down by `credits.expiring` | `runs.live`, against `runs.limit` | `resets.remaining`, against `resets.drawn` |
| Holds back | Creating a verification and moving one along | Creating a verification, and reopening one that had ended | Resetting an `open` or `submitted` verification |
| Refused with | `402` | `409` | `402` |
| Frees up when | The workflow is topped up | One of your verifications ends | The workflow is topped up |

Credits and resets are both topped up by buying, but not by the same purchase, so read the `code` on a
`402` before asking us for more: `credits-exhausted` and `resets-exhausted` are different things to be
short of.

Which of them a reset is held to is decided by the record's status. One reading `open` or `submitted`
is held to the allowance and never to the live ceiling, because it is holding its place already. One
reading `completed` or `cancelled` is held to the live ceiling and never to the allowance, because the
run it clears was charged for - and so is one reading `failed`, which draws nothing because we could
not carry that run through. One reading `gated` is held to both: it has ended, so reopening it takes a
place, and nobody paid for the run it clears. So the record's own `status` tells you which limits a
reset of it could run into before you read anything else.

### When the balance is gone

A workflow reads `remaining` as zero whether its credits were spent, expired or taken back, and in
every case refuses everything that would start a verification or move one along, as `402`:

| Call | On a workflow with no credits left |
| - | - |
| `POST /verifications` | `402` |
| `POST /verifications/instant` | `402` |
| `POST /verifications/{verification_id}/documents` | `402` |
| `DELETE /verifications/{verification_id}/documents/{document_id}` | `402` |
| `POST /verifications/{verification_id}/run` | `402` |
| `POST /verifications/{verification_id}/reset` | `402` |
| Every `GET`, including reading, listing, counting and linking to documents | Works |
| `POST /verifications/{verification_id}/cancel` | Works |
| `POST /verifications/{verification_id}/purge` | Works |
| `POST /verifications/{verification_id}/handoff` | Works, though the journey it opens cannot be walked |

So a workflow that runs out stops moving without ever stopping being readable. Every record on it can
still be read, listed, counted and its documents downloaded, and you can still cancel or purge one.
The verifications already open on it are frozen where they stand until the workflow is topped up, and
nothing about them is lost.

<Warning>
  A hosted journey stops for the person walking it too. They are shown a message asking them to
  contact the organisation that sent them, and are told nothing about your balance or about credits.
  Watch `credits.remaining` rather than letting a workflow run out under a user.
</Warning>

`402` is not transient. Retrying gets the same answer until the workflow is topped up, so treat it as
"ask for more credits" rather than as something to back off and repeat. See
[Retrying safely](/verification/errors#retrying-safely).

### Why `remaining` can read negative

A verification that has not ended has used nothing, so you can be committed to more runs than your
balance covers. When those runs end, each uses the credit it owes, and `remaining` goes below zero.

No more than `runs.limit` verifications can be in that position, so that is as far below zero as it
goes. A top-up settles the debt out of the credits it adds, and `remaining` comes back up.

`credits.expiring` is empty for as long as the balance is negative, a debt standing on no term and
expiring on no date. It fills again with the top-up that settles it.

### When the live ceiling is reached

`runs.limit` is how many verifications a workflow may have `open` or `submitted` at once. Once
`runs.live` has reached it:

* **Creating** a verification is refused with `409`.
* **Resetting one that had already ended** is refused with `409`. Reopening a `completed`, `cancelled`
  or `failed` record takes a place, the same as creating one.
* **Resetting one that is still `open` or `submitted`** goes through. It is holding its place already,
  so reopening it takes no second one.
* **Everything else is untouched.** Uploading, withdrawing, running the checks, handing off and reading
  are not held to the ceiling.

A place frees the moment one of your verifications ends, whether it completed, was cancelled, failed or
expired.

An `instant` workflow is held to the ceiling too. Its verifications are `open` while their checks run
and end when the response is written, so each one holds a place for the length of its own request. In
practice that makes `runs.limit` the number of instant verifications you may have running at once: keep
that many calls in flight and no more, and the ceiling never comes up.

### When the resets run out

Resetting a verification runs its subject through the checks again, which costs us what a run costs.
Whether you have paid for that depends on the run you are clearing:

| You reset a verification reading | The run you clear | So the reset |
| - | - | - |
| `completed`, `cancelled` | Was charged a credit | Draws nothing. You pay for the run you clear and again for the one you open. |
| `failed` | Was never charged, because we could not carry it through | Draws nothing. Our failure is not yours to pay for. |
| `open`, `submitted` | Was never charged | Draws one from `resets.remaining`. |

So `resets` covers only the re-runs nobody has paid for. **Re-verifying a subject you have already
paid for is never held back by it**, however many resets have gone: reset a `completed` record as
often as you need, and each run is charged as a run. Neither is retrying a run we could not finish.

Resets are sold with credits, on the same terms, and read exactly as credits do. A sale carries a
number of them; they are drawn as you clear unpaid runs; and whatever is still standing on a term when
its last day passes is forfeited alongside the credits it came with. `resets.expiring` says what is
standing on what, soonest first.

Once `resets.remaining` reads zero, resetting an `open` or `submitted` verification is refused with
`402` and the code `resets-exhausted`, whichever of the workflow's verifications it is. They are held
by the workflow rather than by any one record, because an unpaid re-run costs us the same wherever it
falls.

Topping up is what fills this, as it is for credits, and resets can be bought on their own, so
running short of room to clear unpaid work does not mean buying runs you do not need. Ask us. What is
already drawn stays drawn: ending, cancelling, expiring or purging a verification gives nothing back,
so `resets.drawn` only ever rises.

Letting a verification end before you reset it is what keeps your resets intact, and that is not a
trick: the credit the ending charges is what pays for the re-run.

Each verification also reports its own `resets`, on its record and in the list it appears in, counting
every reset of it whether or not it drew on the allowance:

```json theme={"system"}
{
  "id": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
  "status": "open",
  "credits_consumed": 1,
  "resets": 1
}
```

A verification reading `2` is on its third run.

An `instant` workflow never draws on this. Its verifications are answered in the request that made
them, so you ask about the subject again rather than resetting a record, and its `resets` stays where
it started.

### Telling the `409`s apart

The live ceiling answers `409`, and so do several things that have nothing to do with credits. The
error's `code` says which you hit. The reset allowance is not among them: it answers `402`, alongside
the balance.

| Call | Code | What it was |
| - | - | - |
| `POST /verifications` | `live-limit-reached` | The workflow holds as many live verifications as it may. |
| | `reference-taken` | That `reference_user_id` is already another user's in this workflow. |
| `POST /verifications/{id}/reset` | `live-limit-reached` | The verification had ended, and reopening it needs a place the workflow has not got. |
| | `verification-expired` | The record's data is deleted; start a new verification instead. |
| | `workflow-not-runnable` | An `instant` verification, which is asked again rather than reset. |

See [Errors](/verification/errors#every-code) for the whole list of codes.

## Running low

Credits, resets and the live ceiling are all set by us on your workflow, so ask us for any of them.
Nothing here is self-serve, and there is no way to buy over the API.

* **Watch `credits.remaining`** on the workflows you run, and alert yourself well before it reaches
  zero. Reading a workflow is free, so there is no cost to checking often.
* **Watch the first entry in `credits.expiring`, not just the total.** It is the next date this
  workflow loses credits on and how many it loses, so it is the one that tells you when to top up.
  Credits expire by term, so `remaining` can drop on a date rather than on a run, and topping up adds
  credits on a new term rather than extending the term of the ones you already hold.
* **Ask for more room on the live ceiling** if `runs.live` sits near `runs.limit` in normal operation.
  How much work you may have in flight is agreed separately from how many runs you bought.
* **Watch `resets.remaining`** if you reset records before they end, and `resets.expiring` for when
  they go. They are sold with credits and expire with them, so a workflow that clears unpaid runs as a
  matter of course needs them stocked like runs. Resetting records that have already completed or been
  cancelled never touches them.
* **Remember that `uat` and `production` workflows are different workflows**, so each carries its own
  credits and its own ceilings. Testing against `uat` never draws down what you bought for real work.
  See [Environments](/verification/authentication#environments).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.