---
title: "billing.credit-hold@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/billing.credit-hold"
updated: null
---

# billing.credit-hold@1 · OpenVibe.Services

HTML: [billing.credit-hold@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/billing.credit-hold)

# BillingCreditHold `billing.credit-hold@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: billing
- Visibility: internal
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/billing/credit-hold.v1.json`](/docs/contracts/billing.credit-hold.json)

billing.credit-hold@1: a reservation against an account's available inference credit. Holds are created before a request runs, may be extended while streaming, and are settled or released exactly once. Money fields are integer nano-USD, as strings.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `hold_id` | string | yes | ULID hold id. | - `pattern ^hld_[0-9A-HJKMNP-TV-Z]{26}$` |
| `subject` | [identity.subject-ref](/docs/contracts/identity.subject-ref) | yes |  |  |
| `project_id` | string \| null |  | The project whose Balance the hold spends. Null or absent for a personal hold on the subject's own Balance (D12); an app subject's hold always names its project. | - `pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$` |
| `credential_id` | string \| null |  | Developer credential the hold is scoped to, if any. | - `pattern ^cred_[0-9A-HJKMNP-TV-Z]{26}$` |
| `held_nano_usd` | `https://openvibe.network/contracts/billing/credit-hold.v1.json#/$defs/money-nano-usd` | yes |  |  |
| `available_nano_usd` | `https://openvibe.network/contracts/billing/credit-hold.v1.json#/$defs/money-nano-usd` | yes |  |  |
| `max_nano_usd` | `https://openvibe.network/contracts/billing/credit-hold.v1.json#/$defs/money-nano-usd` |  |  |  |
| `rate_version` | string |  | Immutable retail rate-card version the hold was priced against. |  |
| `request_fingerprint` | string |  | Opaque fingerprint used to dedupe the same idempotency key. | - `maxLength 1024` |
| `lease_expires_at` | string | yes |  | - `format date-time` |
| `status` | enum |  |  | - `one of "active", "extended", "settled", "released", "expired"` |
| `created_at` | string |  |  | - `format date-time` |
| `service` | string |  | Which metered service spends the credit (D9): ai, run, ... Defaults to ai. | - `pattern ^[a-z][a-z0-9-]{0,39}$` |
| `sandbox` | boolean |  | D10. A sandbox caller's hold draws on that person's sandbox credit for the project (account kind usd\_credit\_sandbox, owner subject:project), never on real Credits, and a real hold never draws on sandbox credit. Sandbox grants (billing.credit-sandbox-grant-request@1) land in that sandbox account. Optional; defaults to false. | - `default false` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: active

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "subject": {
    "type": "user",
    "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "credential_id": "cred_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "held_nano_usd": "100000000",
  "available_nano_usd": "75000000",
  "max_nano_usd": "100000000",
  "rate_version": "rr-2026-11-03-001",
  "request_fingerprint": "fp-chat-001",
  "lease_expires_at": "2026-11-03T01:00:00.000Z",
  "status": "active",
  "created_at": "2026-11-03T00:00:00.000Z"
}
~~~~

Valid: personal-hold

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNPS",
  "subject": {
    "type": "user",
    "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": null,
  "held_nano_usd": "100000000",
  "available_nano_usd": "250000000",
  "max_nano_usd": "100000000",
  "rate_version": "rr-2026-11-03-001",
  "request_fingerprint": "fp-chat-personal-001",
  "lease_expires_at": "2026-11-04T01:00:00.000Z",
  "status": "active",
  "created_at": "2026-11-04T00:00:00.000Z",
  "service": "ai"
}
~~~~

Valid: sandbox

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNP1",
  "subject": {
    "type": "app",
    "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "held_nano_usd": "100000000",
  "available_nano_usd": "50000000",
  "max_nano_usd": "100000000",
  "rate_version": "rr-2026-11-03-001",
  "request_fingerprint": "fp-chat-sandbox-001",
  "lease_expires_at": "2026-11-04T01:00:00.000Z",
  "status": "active",
  "created_at": "2026-11-04T00:00:00.000Z",
  "service": "ai",
  "sandbox": true
}
~~~~

Valid: settled

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNPR",
  "subject": {
    "type": "app",
    "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "credential_id": null,
  "held_nano_usd": "50000000",
  "available_nano_usd": "0",
  "max_nano_usd": "50000000",
  "rate_version": "rr-2026-11-03-001",
  "request_fingerprint": "fp-chat-002",
  "lease_expires_at": "2026-11-03T01:00:00.000Z",
  "status": "settled",
  "created_at": "2026-11-03T00:00:00.000Z"
}
~~~~

Rejected: bad-status

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "subject": {
    "type": "user",
    "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "held_nano_usd": "100000000",
  "available_nano_usd": "75000000",
  "lease_expires_at": "2026-11-03T01:00:00.000Z",
  "status": "closed"
}
~~~~

Rejected: money-float

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "subject": {
    "type": "user",
    "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "held_nano_usd": "100000000.5",
  "available_nano_usd": "75000000",
  "lease_expires_at": "2026-11-03T01:00:00.000Z"
}
~~~~

Rejected: money-number

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "subject": {
    "type": "user",
    "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "held_nano_usd": 100000000,
  "available_nano_usd": "75000000",
  "lease_expires_at": "2026-11-03T01:00:00.000Z"
}
~~~~

Rejected: sandbox-string

~~~~
{
  "hold_id": "hld_01JAB2C3D4E5F6G7H8J9K0MNP2",
  "subject": {
    "type": "app",
    "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "held_nano_usd": "100000000",
  "available_nano_usd": "50000000",
  "max_nano_usd": "100000000",
  "rate_version": "rr-2026-11-03-001",
  "request_fingerprint": "fp-chat-sandbox-002",
  "lease_expires_at": "2026-11-04T01:00:00.000Z",
  "status": "active",
  "created_at": "2026-11-04T00:00:00.000Z",
  "service": "ai",
  "sandbox": "yes"
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('billing.credit-hold@1', value);   // { valid, errors: [{ path, message }] }
~~~~
