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

# billing.vibes-charge@1 · OpenVibe.Services

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

# BillingVibesCharge `billing.vibes-charge@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: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/billing/vibes-charge.v1.json`](/docs/contracts/billing.vibes-charge.json)

billing.vibes-charge@1: one Vibes Pay charge (D12). An app creates it for a person to pay with Vibes; GET /api/v1/vibes/charges/:id answers it, and a replay of the same app reference returns the same charge. amount is a whole number of Vibes (never a fraction, never nano-USD). status moves pending -> paid -> refunded, or pending -> expired|cancelled. sandbox charges use test Vibes only, worth nothing. The paid and refunded webhook/event payloads carry the same fields plus an opaque payer\_ref and no person id or email.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `id` | string | yes | ULID charge id. | - `pattern ^vch_[0-9A-HJKMNP-TV-Z]{26}$` |
| `app` | string | yes | The app that created the charge (its app\_ id). | - `pattern ^app_[0-9A-HJKMNP-TV-Z]{26}$` |
| `project_id` | string | yes | The app's project; paid Vibes land in that project's Vibes wallet. | - `pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$` |
| `amount` | integer | yes | Price in whole Vibes. A positive integer; Vibes have no fractions. | - `minimum 1` |
| `description` | string | yes | What the person is paying for. | - `minLength 1`<br>- `maxLength 140` |
| `reference` | string | yes | The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge. | - `minLength 1`<br>- `maxLength 128` |
| `status` | enum | yes |  | - `one of "pending", "paid", "refunded", "expired", "cancelled"` |
| `pay_url` | string | yes | OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch\_... | - `format uri` |
| `return_url` | string \| null | yes | Where the browser returns after paying; null when the app gave no return URL. | - `format uri` |
| `created_at` | string | yes |  | - `format date-time` |
| `paid_at` | string \| null | yes | When the Vibes moved, or null while the charge is unpaid. | - `format date-time` |
| `refunded_at` | string \| null | yes | When the Vibes went back to the person, or null. | - `format date-time` |
| `expires_at` | string | yes | The pay sheet stops accepting payment after this instant. | - `format date-time` |
| `sandbox` | boolean | yes | True for a sandbox app: sandbox Vibes only, worth nothing. |  |

## Examples

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

 Valid: expired

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "expired",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Valid: paid-sandbox

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "paid",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": null,
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": true
}
~~~~

Valid: pending

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "pending",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Valid: refunded

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "refunded",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": "2026-11-04T12:01:00.000Z",
  "refunded_at": "2026-11-04T13:00:00.000Z",
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Rejected: bad-id

~~~~
{
  "id": "vch_1",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "pending",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Rejected: description-too-long

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "reference": "order_123",
  "status": "pending",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Rejected: float-amount

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 12.5,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "pending",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Rejected: person-id

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "pending",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false,
  "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}
~~~~

Rejected: unknown-status

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 50,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "failed",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

Rejected: zero-amount

~~~~
{
  "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "amount": 0,
  "description": "Sticker pack: Neon cats",
  "reference": "order_123",
  "status": "pending",
  "pay_url": "https://openvibe.network/pay/vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "return_url": "https://myapp.example/thanks",
  "created_at": "2026-11-04T12:00:00.000Z",
  "paid_at": null,
  "refunded_at": null,
  "expires_at": "2026-11-04T12:30:00.000Z",
  "sandbox": false
}
~~~~

## Validate

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