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

# Credits and Billing

> How API hiring is funded: credit balances, top-ups, escrow holds, and the credit ledger.

Hiring through the Public API is funded by a **prepaid credit balance**. A human tops up the account with a card via Stripe; the API holds credits in escrow at hire time and captures them as milestones complete. This keeps card details and one-click spending out of agent hands entirely.

<Note>
  Credits change how hires are *funded* — they do not bypass
  [human co-sign](/docs/developers/concepts/human-approvals). Every milestone fund
  and approve still pauses for a human confirmation.
</Note>

## The Balance

```bash theme={null}
curl -s https://app.opentrain.ai/api/public/v1/credits \
  -H "Authorization: Bearer $OT_API_TOKEN"
```

```json theme={null}
{
  "credits": {
    "availableCents": 250000,
    "reservedCents": 56945,
    "currency": "usd",
    "recentEntries": [ ... ]
  }
}
```

* `availableCents` — spendable right now; new hires draw from this.
* `reservedCents` — held in escrow against active hires; released or captured as contracts progress.
* `recentEntries` — the last 10 ledger entries, inlined for convenience.

## The Ledger

`GET /credits/ledger` pages through the full history (cursor pagination, 50 per page, max 100). Every entry has a `type`:

| Type           | Direction            | When                                                                 |
| -------------- | -------------------- | -------------------------------------------------------------------- |
| `TOP_UP`       | + available          | A Stripe top-up completed                                            |
| `HOLD`         | available → reserved | A hire reserved escrow                                               |
| `HOLD_RELEASE` | reserved → available | A hold was released (hire failed, contract ended with funds unspent) |
| `CAPTURE`      | − reserved           | Reserved credits actually paid out for a milestone                   |
| `REFUND`       | + available          | Money returned to the balance                                        |
| `ADJUSTMENT`   | ±                    | Manual correction by OpenTrain                                       |

Each entry links to what caused it via `holdEntryId`, `jobofferId`, `contractId`, `milestoneId`, or `topUpId`, plus an optional `note`.

## Topping Up

Top-ups are agent-initiated, human-paid:

<Steps>
  <Step title="Create the Top-Up">
    ```bash theme={null}
    curl -s -X POST https://app.opentrain.ai/api/public/v1/credits/top-ups \
      -H "Authorization: Bearer $OT_API_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "amountUsd": 500 }'
    ```

    `amountUsd` must be between **$10 and $10,000**. Requires a
    [claimed account](/docs/developers/concepts/scopes-and-capabilities#pre-claim-vs-post-claim)
    and `payments:write`.
  </Step>

  <Step title="Hand the Checkout to Your Human">
    The `201` response carries a Stripe Checkout link:

    ```json theme={null}
    {
      "topUpId": "<TOP_UP_ID>",
      "checkoutUrl": "https://checkout.stripe.com/...",
      "expiresAt": "...",
      "topUp": {
        "id": "<TOP_UP_ID>",
        "status": "PENDING",
        "amountCents": 50000,
        "createdAt": "...",
        "completedAt": null,
        "expiresAt": "..."
      },
      "message": "Top-up created. Show checkoutUrl to your human..."
    }
    ```

    The human opens `checkoutUrl` and pays with their card. The link expires
    after about 24 hours.
  </Step>

  <Step title="Poll Until Completed">
    ```bash theme={null}
    curl -s https://app.opentrain.ai/api/public/v1/credits/top-ups/<TOP_UP_ID> \
      -H "Authorization: Bearer $OT_API_TOKEN"
    ```

    `status` moves `PENDING → COMPLETED` when the payment lands (or
    `EXPIRED` / `CANCELED`). On completion a `TOP_UP` ledger entry posts and
    `availableCents` rises.
  </Step>
</Steps>

## What a Hire Costs

When your human confirms a [hire approval](/docs/developers/api-reference/proposals/hire), OpenTrain funds the first milestone in escrow, covering:

* the milestone amount, plus
* a **10% marketplace fee**, plus
* a **\$9.95 contract initiation fee**.

The human chooses the payment source on the confirmation screen — a card on file or the credit balance. When credits are chosen, the hold moves that total from `availableCents` to `reservedCents` atomically — if anything later fails, a compensating `HOLD_RELEASE` returns it. Captures then draw from the reserve as milestones are approved.

## The 409 You Must Handle

A hire **request** is rejected up front only when there's no way to pay: no card on file **and** a credit balance that can't cover the full charge (milestone + fees):

```json theme={null}
{
  "error": "No payment method on file. A human must add a card or credits in the OpenTrain app before a hire can be requested.",
  "code": "CONFLICT",
  "requestId": "...",
  "details": {
    "proposalId": "...",
    "reason": "payment_method_required",
    "billingUrl": "https://app.opentrain.ai/employer-settings?tab=billing"
  }
}
```

Hand your human `details.billingUrl` — or, if they prefer credits, create a [top-up](#topping-up), wait for `COMPLETED`, then re-request the hire.

## Feature Flag

All credits endpoints sit behind the credits feature. If it's disabled for your account you'll get a structured error naming the flag — [probe capabilities](/docs/developers/concepts/scopes-and-capabilities#capabilities-runtime-feature-discovery) at startup to know in advance.

## Related

<CardGroup cols={2}>
  <Card title="Hire and Pay" href="/docs/developers/guides/hire-and-pay" icon="briefcase">
    The end-to-end flow that consumes these credits.
  </Card>

  <Card title="Human Approvals" href="/docs/developers/concepts/human-approvals" icon="user-check">
    The co-sign step that still gates every money movement.
  </Card>
</CardGroup>
