openvibers/openvibe.contracts Commits
0.289.0: Balance and Vibes Pay contracts (D12) (#298)
Changes
@@@ -1,6 +1,39 @@# Changelog++## 0.289.0 — 2026-11-04++++- **Balance and Vibes Pay (AI Exchange D12, MONEY.md build order step 1).**++ - **Vibes Pay schemas:** `billing.vibes-charge@1` (id `vch_<ULID>`, app, project_id, `amount` in whole Vibes,++ a 1–140 description, the app's reference, status pending|paid|refunded|expired|cancelled, pay_url, return_url,++ the timestamps and sandbox), `billing.vibes-charge-create-request@1` (`amount`, `description`, `reference` — the++ app's idempotency key — and an optional `return_url`) and `billing.vibes-charge-refund-request@1` (an optional++ `reason`; a refund is always for the whole charge).++ - **Vibes Pay events:** `billing.vibes.charge.paid@1` and `billing.vibes.charge.refunded@1` are also the signed++ webhook bodies (HMAC-SHA256, retried with backoff): the charge fields plus an opaque `payer_ref` that is stable++ per app, and deliberately no person id and no email. Billing's manifest lists both in `eventsProduced`.++ - **Balance:** `billing.balance@1` gains the D12 Balance branch — `owner` (user or project), `balance_nano_usd`,++ `held_nano_usd`, `available_nano_usd` and an optional `sandbox`, with money as integer nano-USD decimal strings++ (D1, never a float) — answered by `GET /api/me/balance`. The contract id is permanent, so the Vibes wallet body++ that `billing.balance.read` already answered stays as the second branch; both branches are closed with++ `additionalProperties: false`, and new code sends the Balance branch.++ - **Balance requests:** `billing.balance-transfer-request@1` (`to_project_id`, positive `amount_nano_usd`,++ `idempotency_key`) for moving a person's Balance to a project's, and++ `billing.vibes-purchase-from-balance-request@1` (`vibes` as a whole positive integer, `idempotency_key`) for++ Get Vibes with Balance.++ - **Vibes read:** `billing.vibes-wallet@1` is the person's own Vibes — credit, creator payable and bits_per_usd,++ never a Balance — answered by `GET /api/v1/vibes/me`.++ - **Capabilities:** `billing.vibes.charge` (public, grantable to apps, a sandbox app included, which charges++ sandbox Vibes only) implemented by `POST /api/v1/vibes/charges`, `GET /api/v1/vibes/charges/:id` and++ `POST /api/v1/vibes/charges/:id/refund`; `billing.vibes.read` (public, a person-delegated read of their own++ Vibes for an app they approved) implemented by `GET /api/v1/vibes/me`.++ - **Personal holds:** `billing.credit-hold-create-request@1` no longer requires `project_id`; without it the hold++ is personal and spends the subject's own Balance. An `app` subject must still name its project (`if`/`then`),++ so the accepted set only grows and the compat gate stays green.++ - Valid and invalid fixtures cover both Balance branches, whole-number Vibes, D1 decimal-string money, a personal++ hold, an app subject without a project, a partial-refund body, and webhook payloads with a person id or an++ email-shaped payer reference.++## 0.288.0 — 2026-11-04- **Configuration contracts (AI Exchange D11, S1), built on openvibe-shared/config:@@@ -2,7 +2,7 @@> Machine-readable contracts for the whole OpenVibe network.--**Status:** alpha (first released in Wave 1; the release is package.json's `version`, tagged). At v0.288.0: 978 schemas in `contracts/catalog.json`, 60 service manifests, 450 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin). `node scripts/docs-currency.js <root>` checks every checkout's README and `STATUS.json` against its code: the pins and versions they name, a library's consumer list, the deployed flag against the manifest, README links, and the paths and migrations they name. `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).++**Status:** alpha (first released in Wave 1; the release is package.json's `version`, tagged). At v0.289.0: 986 schemas in `contracts/catalog.json`, 60 service manifests, 452 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin). `node scripts/docs-currency.js <root>` checks every checkout's README and `STATUS.json` against its code: the pins and versions they name, a library's consumer list, the deployed flag against the manifest, README links, and the paths and migrations they name. `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).**Plan:** OpenVibe Development Roadmap, Wave 1 (implementation plan rev 3, §3.1 and §18.11).**License:** AGPL-3.0 (same as every OpenVibe service).nnew file mode 100644@@@ -0,0 +1,30 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/balance-transfer-request.v1.json",++ "title": "BillingBalanceTransferRequest",++ "description": "billing.balance-transfer-request@1: body of POST /api/me/balance/transfers (D12). Moves amount_nano_usd from the signed-in person's Balance to a project they administer, into that project's Balance. Money is positive integer nano-USD as a decimal string (D1), never a float; the transfer is refused when the Balance or the person's project role is short. idempotency_key makes the transfer exactly once: a replay with the same key and body returns the first answer and moves nothing.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "to_project_id",++ "amount_nano_usd",++ "idempotency_key"++ ],++ "properties": {++ "to_project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The project whose Balance receives the money; the person must administer it."++ },++ "amount_nano_usd": {++ "type": "string",++ "pattern": "^[1-9][0-9]{0,18}$",++ "description": "A positive integer nano-USD (1e-9 USD) as a decimal string (D1), never a float."++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9._:-]{8,128}$",++ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."++ }++ }++}@@@ -2,16 +2,145 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/billing/balance.v1.json","title": "BillingBalance",-- "description": "billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.",++ "description": "billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.","type": "object",-- "required": ["subject", "currency", "credit", "payable", "pending_payouts", "payable_value_cents", "bits_per_usd"],"properties": {-- "subject": { "$ref": "../identity/subject-ref.v1.json" },-- "currency": { "const": "vibes-bits" },-- "credit": { "type": "integer" },-- "payable": { "type": "integer" },-- "pending_payouts": { "type": "integer" },-- "payable_value_cents": { "type": "integer", "description": "What payable is worth in US cents at today's rate." },-- "bits_per_usd": { "type": "number" }-- }++ "subject": {++ "$ref": "../identity/subject-ref.v1.json"++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer"++ },++ "payable": {++ "type": "integer"++ },++ "pending_payouts": {++ "type": "integer"++ },++ "payable_value_cents": {++ "type": "integer",++ "description": "What payable is worth in US cents at today's rate."++ },++ "bits_per_usd": {++ "type": "number"++ }++ },++ "$defs": {++ "money-nano-usd": {++ "type": "string",++ "pattern": "^[0-9]{1,19}$",++ "description": "Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float."++ },++ "balance-owner": {++ "oneOf": [++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "A person: usd_credit owned by user:<usr_id>."++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "project"++ },++ "id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "A project: usd_credit owned by project:<prj_id>."++ }++ ]++ }++ },++ "oneOf": [++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "owner",++ "balance_nano_usd",++ "held_nano_usd",++ "available_nano_usd"++ ],++ "properties": {++ "owner": {++ "$ref": "#/$defs/balance-owner"++ },++ "balance_nano_usd": {++ "$ref": "#/$defs/money-nano-usd"++ },++ "held_nano_usd": {++ "$ref": "#/$defs/money-nano-usd"++ },++ "available_nano_usd": {++ "$ref": "#/$defs/money-nano-usd"++ },++ "sandbox": {++ "type": "boolean",++ "default": false,++ "description": "True for a sandbox account (D10): sandbox credit only, never real money."++ }++ }++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "subject",++ "currency",++ "credit",++ "payable",++ "pending_payouts",++ "payable_value_cents",++ "bits_per_usd"++ ],++ "properties": {++ "subject": {++ "$ref": "../identity/subject-ref.v1.json"++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer"++ },++ "payable": {++ "type": "integer"++ },++ "pending_payouts": {++ "type": "integer"++ },++ "payable_value_cents": {++ "type": "integer",++ "description": "What payable is worth in US cents at today's rate."++ },++ "bits_per_usd": {++ "type": "number"++ }++ }++ }++ ]}@@@ -2,17 +2,45 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/billing/credit-hold-create-request.v1.json","title": "BillingCreditHoldCreateRequest",-- "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.",++ "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.","type": "object","required": ["idempotency_key","subject",-- "project_id","max_nano_usd","rate_version","request_fingerprint"],"additionalProperties": false,++ "allOf": [++ {++ "if": {++ "type": "object",++ "properties": {++ "subject": {++ "type": "object",++ "properties": {++ "type": {++ "const": "app"++ }++ },++ "required": [++ "type"++ ]++ }++ },++ "required": [++ "subject"++ ]++ },++ "then": {++ "type": "object",++ "required": [++ "project_id"++ ]++ }++ }++ ],"$defs": {"money-nano-usd": {"type": "string",@@@ -30,7 +58,8 @@},"project_id": {"type": "string",-- "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app."},"credential_id": {"type": [nnew file mode 100644@@@ -0,0 +1,37 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-charge-create-request.v1.json",++ "title": "BillingVibesChargeCreateRequest",++ "description": "billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "amount",++ "description",++ "reference"++ ],++ "properties": {++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference; it is the idempotency key."++ },++ "return_url": {++ "type": "string",++ "format": "uri",++ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-charge-refund-request.v1.json",++ "title": "BillingVibesChargeRefundRequest",++ "description": "billing.vibes-charge-refund-request@1: the optional body of POST /api/v1/vibes/charges/:id/refund. A refund is always for the whole charge: the Vibes go back from the project's Vibes wallet to the person. reason is recorded on the refund. No amount may be given, because a partial refund is not a Vibes Pay refund.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 300,++ "description": "Why the app refunded, for its own and the person's record."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,109 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-charge.v1.json",++ "title": "BillingVibesCharge",++ "description": "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.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "pay_url",++ "return_url",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "ULID charge id."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge (its app_ id)."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project; paid Vibes land in that project's Vibes wallet."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes. A positive integer; Vibes have no fractions."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge."++ },++ "status": {++ "enum": [++ "pending",++ "paid",++ "refunded",++ "expired",++ "cancelled"++ ]++ },++ "pay_url": {++ "type": "string",++ "format": "uri",++ "description": "OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_..."++ },++ "return_url": {++ "type": [++ "string",++ "null"++ ],++ "format": "uri",++ "description": "Where the browser returns after paying; null when the app gave no return URL."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When the Vibes moved, or null while the charge is unpaid."++ },++ "refunded_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When the Vibes went back to the person, or null."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time",++ "description": "The pay sheet stops accepting payment after this instant."++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,24 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-purchase-from-balance-request.v1.json",++ "title": "BillingVibesPurchaseFromBalanceRequest",++ "description": "billing.vibes-purchase-from-balance-request@1: body of POST /api/me/vibes/from-balance (D12). Buys a whole number of Vibes with the signed-in person's Balance at the same Vibes price as a pack, charging exact nano-USD; Vibes -> Balance is never allowed. vibes is a whole integer (Vibes have no fractions); the money side is integer nano-USD (D1), never a float. idempotency_key makes the purchase exactly once.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "vibes",++ "idempotency_key"++ ],++ "properties": {++ "vibes": {++ "type": "integer",++ "minimum": 1,++ "description": "How many whole Vibes to buy."++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9._:-]{8,128}$",++ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,52 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-wallet.v1.json",++ "title": "BillingVibesWallet",++ "description": "billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "subject",++ "currency",++ "credit",++ "payable",++ "bits_per_usd"++ ],++ "properties": {++ "subject": {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "The person the app reads for (the approved delegation). Only a user, never an app or a service."++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes to spend."++ },++ "payable": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes received as a creator, to cash out; never Balance."++ },++ "bits_per_usd": {++ "type": "number",++ "description": "Today's Vibes per USD rate, the same one a pack is priced with."++ }++ }++}@@@ -2295,6 +2295,78 @@"compatibility": "backward","status": "active"},++ {++ "id": "billing.vibes-charge",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/vibes-charge.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.vibes-charge-create-request",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/vibes-charge-create-request.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.vibes-charge-refund-request",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/vibes-charge-refund-request.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.vibes-wallet",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/vibes-wallet.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.balance-transfer-request",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/balance-transfer-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.vibes-purchase-from-balance-request",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/vibes-purchase-from-balance-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.vibes.charge.paid",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "events/payloads/billing.vibes.charge.paid.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "billing.vibes.charge.refunded",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "events/payloads/billing.vibes.charge.refunded.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },{"id": "billing.cashout-decision","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,87 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.paid.v1.json",++ "title": "BillingVibesChargePaidPayload",++ "description": "billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox",++ "payer_ref"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project, whose Vibes wallet received the charge."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "The whole number of Vibes paid."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's reference, as given when the charge was created."++ },++ "status": {++ "const": "paid"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the Vibes moved from the person to the project."++ },++ "refunded_at": {++ "type": "null",++ "description": "Null: a paid charge has not been refunded."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ },++ "payer_ref": {++ "type": "string",++ "minLength": 8,++ "maxLength": 128,++ "pattern": "^[A-Za-z0-9._:-]+$",++ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,88 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.refunded.v1.json",++ "title": "BillingVibesChargeRefundedPayload",++ "description": "billing.vibes.charge.refunded v1 (OpenVibe.Billing, written when a Vibes Pay charge is refunded): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox",++ "payer_ref"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project, whose Vibes wallet received the charge."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "The whole number of Vibes paid."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's reference, as given when the charge was created."++ },++ "status": {++ "const": "refunded"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the Vibes moved from the person to the project."++ },++ "refunded_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the Vibes went back to the person."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ },++ "payer_ref": {++ "type": "string",++ "minLength": 8,++ "maxLength": 128,++ "pattern": "^[A-Za-z0-9._:-]+$",++ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount_nano_usd": "5000000000",++ "idempotency_key": "transfer-key-001",++ "to_user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "amount_nano_usd": "5000000000",++ "idempotency_key": "transfer-key-001"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount_nano_usd": 5000000000,++ "idempotency_key": "transfer-key-001"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount_nano_usd": "0",++ "idempotency_key": "transfer-key-001"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount_nano_usd": "5000000000",++ "idempotency_key": "transfer-key-001"++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "1",++ "held_nano_usd": "0",++ "available_nano_usd": "1",++ "promo_bits": 5++}nnew file mode 100644@@@ -0,0 +1,8 @@++{++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "12400000000",++ "held_nano_usd": "0"++}nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "1",++ "held_nano_usd": "0",++ "available_nano_usd": "1",++ "subject": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "currency": "vibes-bits",++ "credit": 1,++ "payable": 0,++ "pending_payouts": 0,++ "payable_value_cents": 0,++ "bits_per_usd": 100++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": 12400000000,++ "held_nano_usd": "0",++ "available_nano_usd": "12400000000"++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "owner": {++ "type": "guest",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "1",++ "held_nano_usd": "0",++ "available_nano_usd": "1"++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "owner": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "12400000000",++ "held_nano_usd": "0",++ "available_nano_usd": "12400000000",++ "sandbox": false++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "owner": {++ "type": "project",++ "id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "5000000000",++ "held_nano_usd": "1200000000",++ "available_nano_usd": "3800000000"++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "owner": {++ "type": "project",++ "id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "balance_nano_usd": "100000000",++ "held_nano_usd": "0",++ "available_nano_usd": "100000000",++ "sandbox": true++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "idempotency_key": "hold-key-app-001",++ "subject": {++ "type": "app",++ "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "max_nano_usd": "50000000",++ "rate_version": "rr-2026-11-03-001",++ "request_fingerprint": "fp-chat-app-001"++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "idempotency_key": "hold-key-personal-001",++ "subject": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "max_nano_usd": "100000000",++ "rate_version": "rr-2026-11-03-001",++ "request_fingerprint": "fp-chat-personal-001",++ "service": "ai"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "amount": 50,++ "description": "",++ "reference": "order_123"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "amount": 0.5,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "amount": 50,++ "description": "Sticker pack: Neon cats"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "return_url": "not a url"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "amount": 1,++ "description": "One cheer",++ "reference": "order_124"++}nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "return_url": "https://myapp.example/thanks"++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "amount": 25++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "reason": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"++}nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "reason": "The sticker pack was removed"++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "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"++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "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++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "vibes": 12.5,++ "idempotency_key": "vibes-buy-001"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "vibes": "1250",++ "idempotency_key": "vibes-buy-001"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "vibes": 0,++ "idempotency_key": "vibes-buy-001"++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "vibes": 1250,++ "idempotency_key": "vibes-buy-001"++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "subject": {++ "type": "app",++ "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "currency": "vibes-bits",++ "credit": 0,++ "payable": 0,++ "bits_per_usd": 100++}nnew file mode 100644@@@ -0,0 +1,11 @@++{++ "subject": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "currency": "vibes-bits",++ "credit": 1250,++ "payable": 0,++ "bits_per_usd": 100,++ "balance_nano_usd": "1"++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "subject": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "currency": "vibes-bits",++ "credit": -1,++ "payable": 0,++ "bits_per_usd": 100++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "subject": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "currency": "usd-cents",++ "credit": 1250,++ "payable": 0,++ "bits_per_usd": 100++}nnew file mode 100644@@@ -0,0 +1,10 @@++{++ "subject": {++ "type": "user",++ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++ },++ "currency": "vibes-bits",++ "credit": 1250,++ "payable": 340,++ "bits_per_usd": 100++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50.5,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "paid",++ "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": false,++ "payer_ref": "payer_7f3c9a12"++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "paid",++ "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": false,++ "payer_ref": "[email protected]"++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "paid",++ "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": false,++ "payer_ref": "payer_7f3c9a12",++ "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "paid",++ "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": false,++ "payer_ref": "payer_7f3c9a12"++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "paid",++ "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,++ "payer_ref": "payer_7f3c9a12"++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "refunded",++ "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,++ "payer_ref": "payer_7f3c9a12",++ "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "refunded",++ "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": false,++ "payer_ref": "payer_7f3c9a12"++}nnew file mode 100644@@@ -0,0 +1,15 @@++{++ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "amount": 50,++ "description": "Sticker pack: Neon cats",++ "reference": "order_123",++ "status": "refunded",++ "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,++ "payer_ref": "payer_7f3c9a12"++}@@@ -26246,17 +26246,8 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/billing/balance.v1.json","title": "BillingBalance",-- "description": "billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.",++ "description": "billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.","type": "object",-- "required": [-- "subject",-- "currency",-- "credit",-- "payable",-- "pending_payouts",-- "payable_value_cents",-- "bits_per_usd"-- ],"properties": {"subject": {"$ref": "../identity/subject-ref.v1.json"@@@ -26280,6 +26271,606 @@"bits_per_usd": {"type": "number"}++ },++ "$defs": {++ "money-nano-usd": {++ "type": "string",++ "pattern": "^[0-9]{1,19}$",++ "description": "Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float."++ },++ "balance-owner": {++ "oneOf": [++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "A person: usd_credit owned by user:<usr_id>."++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "project"++ },++ "id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "A project: usd_credit owned by project:<prj_id>."++ }++ ]++ }++ },++ "oneOf": [++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "owner",++ "balance_nano_usd",++ "held_nano_usd",++ "available_nano_usd"++ ],++ "properties": {++ "owner": {++ "$ref": "#/$defs/balance-owner"++ },++ "balance_nano_usd": {++ "$ref": "#/$defs/money-nano-usd"++ },++ "held_nano_usd": {++ "$ref": "#/$defs/money-nano-usd"++ },++ "available_nano_usd": {++ "$ref": "#/$defs/money-nano-usd"++ },++ "sandbox": {++ "type": "boolean",++ "default": false,++ "description": "True for a sandbox account (D10): sandbox credit only, never real money."++ }++ }++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "subject",++ "currency",++ "credit",++ "payable",++ "pending_payouts",++ "payable_value_cents",++ "bits_per_usd"++ ],++ "properties": {++ "subject": {++ "$ref": "../identity/subject-ref.v1.json"++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer"++ },++ "payable": {++ "type": "integer"++ },++ "pending_payouts": {++ "type": "integer"++ },++ "payable_value_cents": {++ "type": "integer",++ "description": "What payable is worth in US cents at today's rate."++ },++ "bits_per_usd": {++ "type": "number"++ }++ }++ }++ ]++ }++ },++ "billing.vibes-charge": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-charge.v1.json",++ "title": "BillingVibesCharge",++ "description": "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.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "pay_url",++ "return_url",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "ULID charge id."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge (its app_ id)."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project; paid Vibes land in that project's Vibes wallet."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes. A positive integer; Vibes have no fractions."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge."++ },++ "status": {++ "enum": [++ "pending",++ "paid",++ "refunded",++ "expired",++ "cancelled"++ ]++ },++ "pay_url": {++ "type": "string",++ "format": "uri",++ "description": "OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_..."++ },++ "return_url": {++ "type": [++ "string",++ "null"++ ],++ "format": "uri",++ "description": "Where the browser returns after paying; null when the app gave no return URL."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When the Vibes moved, or null while the charge is unpaid."++ },++ "refunded_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When the Vibes went back to the person, or null."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time",++ "description": "The pay sheet stops accepting payment after this instant."++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ }++ }++ }++ },++ "billing.vibes-charge-create-request": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-charge-create-request.v1.json",++ "title": "BillingVibesChargeCreateRequest",++ "description": "billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "amount",++ "description",++ "reference"++ ],++ "properties": {++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference; it is the idempotency key."++ },++ "return_url": {++ "type": "string",++ "format": "uri",++ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."++ }++ }++ }++ },++ "billing.vibes-charge-refund-request": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-charge-refund-request.v1.json",++ "title": "BillingVibesChargeRefundRequest",++ "description": "billing.vibes-charge-refund-request@1: the optional body of POST /api/v1/vibes/charges/:id/refund. A refund is always for the whole charge: the Vibes go back from the project's Vibes wallet to the person. reason is recorded on the refund. No amount may be given, because a partial refund is not a Vibes Pay refund.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 300,++ "description": "Why the app refunded, for its own and the person's record."++ }++ }++ }++ },++ "billing.vibes-wallet": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-wallet.v1.json",++ "title": "BillingVibesWallet",++ "description": "billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "subject",++ "currency",++ "credit",++ "payable",++ "bits_per_usd"++ ],++ "properties": {++ "subject": {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "The person the app reads for (the approved delegation). Only a user, never an app or a service."++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes to spend."++ },++ "payable": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes received as a creator, to cash out; never Balance."++ },++ "bits_per_usd": {++ "type": "number",++ "description": "Today's Vibes per USD rate, the same one a pack is priced with."++ }++ }++ }++ },++ "billing.balance-transfer-request": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/balance-transfer-request.v1.json",++ "title": "BillingBalanceTransferRequest",++ "description": "billing.balance-transfer-request@1: body of POST /api/me/balance/transfers (D12). Moves amount_nano_usd from the signed-in person's Balance to a project they administer, into that project's Balance. Money is positive integer nano-USD as a decimal string (D1), never a float; the transfer is refused when the Balance or the person's project role is short. idempotency_key makes the transfer exactly once: a replay with the same key and body returns the first answer and moves nothing.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "to_project_id",++ "amount_nano_usd",++ "idempotency_key"++ ],++ "properties": {++ "to_project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The project whose Balance receives the money; the person must administer it."++ },++ "amount_nano_usd": {++ "type": "string",++ "pattern": "^[1-9][0-9]{0,18}$",++ "description": "A positive integer nano-USD (1e-9 USD) as a decimal string (D1), never a float."++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9._:-]{8,128}$",++ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."++ }++ }++ }++ },++ "billing.vibes-purchase-from-balance-request": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/vibes-purchase-from-balance-request.v1.json",++ "title": "BillingVibesPurchaseFromBalanceRequest",++ "description": "billing.vibes-purchase-from-balance-request@1: body of POST /api/me/vibes/from-balance (D12). Buys a whole number of Vibes with the signed-in person's Balance at the same Vibes price as a pack, charging exact nano-USD; Vibes -> Balance is never allowed. vibes is a whole integer (Vibes have no fractions); the money side is integer nano-USD (D1), never a float. idempotency_key makes the purchase exactly once.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "vibes",++ "idempotency_key"++ ],++ "properties": {++ "vibes": {++ "type": "integer",++ "minimum": 1,++ "description": "How many whole Vibes to buy."++ },++ "idempotency_key": {++ "type": "string",++ "pattern": "^[A-Za-z0-9._:-]{8,128}$",++ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."++ }++ }++ }++ },++ "billing.vibes.charge.paid": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.paid.v1.json",++ "title": "BillingVibesChargePaidPayload",++ "description": "billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox",++ "payer_ref"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project, whose Vibes wallet received the charge."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "The whole number of Vibes paid."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's reference, as given when the charge was created."++ },++ "status": {++ "const": "paid"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the Vibes moved from the person to the project."++ },++ "refunded_at": {++ "type": "null",++ "description": "Null: a paid charge has not been refunded."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ },++ "payer_ref": {++ "type": "string",++ "minLength": 8,++ "maxLength": 128,++ "pattern": "^[A-Za-z0-9._:-]+$",++ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."++ }++ }++ }++ },++ "billing.vibes.charge.refunded": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.refunded.v1.json",++ "title": "BillingVibesChargeRefundedPayload",++ "description": "billing.vibes.charge.refunded v1 (OpenVibe.Billing, written when a Vibes Pay charge is refunded): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox",++ "payer_ref"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project, whose Vibes wallet received the charge."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "The whole number of Vibes paid."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's reference, as given when the charge was created."++ },++ "status": {++ "const": "refunded"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the Vibes moved from the person to the project."++ },++ "refunded_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the Vibes went back to the person."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time"++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ },++ "payer_ref": {++ "type": "string",++ "minLength": 8,++ "maxLength": 128,++ "pattern": "^[A-Za-z0-9._:-]+$",++ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."++ }}}},@@@ -28052,17 +28643,45 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/billing/credit-hold-create-request.v1.json","title": "BillingCreditHoldCreateRequest",-- "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.",++ "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.","type": "object","required": ["idempotency_key","subject",-- "project_id","max_nano_usd","rate_version","request_fingerprint"],"additionalProperties": false,++ "allOf": [++ {++ "if": {++ "type": "object",++ "properties": {++ "subject": {++ "type": "object",++ "properties": {++ "type": {++ "const": "app"++ }++ },++ "required": [++ "type"++ ]++ }++ },++ "required": [++ "subject"++ ]++ },++ "then": {++ "type": "object",++ "required": [++ "project_id"++ ]++ }++ }++ ],"$defs": {"money-nano-usd": {"type": "string",@@@ -28080,7 +28699,8 @@},"project_id": {"type": "string",-- "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app."},"credential_id": {"type": [@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bench API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -1933,6 +1933,279 @@}}},++ "/api/v1/vibes/charges": {++ "post": {++ "operationId": "post_api_v1_vibes_charges",++ "summary": "Sell something for Vibes as a developer app (Vibes Pay, D12).",++ "description": "**billing.vibes.charge** (public): Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",++ "tags": [++ "billing.vibes"++ ],++ "x-openvibe-capabilities": [++ "billing.vibes.charge"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "billing-vibes-write",++ "security": [++ {++ "openvibeToken": [++ "billing.vibes.charge"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.vibes-charge-create-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.vibes-charge.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/vibes/charges/{id}": {++ "get": {++ "operationId": "get_api_v1_vibes_charges_id",++ "summary": "Sell something for Vibes as a developer app (Vibes Pay, D12).",++ "description": "**billing.vibes.charge** (public): Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",++ "tags": [++ "billing.vibes"++ ],++ "x-openvibe-capabilities": [++ "billing.vibes.charge"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "billing-vibes-write",++ "security": [++ {++ "openvibeToken": [++ "billing.vibes.charge"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/billing.vibes-charge-create-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "amount",++ "in": "query",++ "required": true,++ "schema": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."++ },++ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."++ },++ {++ "name": "description",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "description": "What the person is paying for."++ },++ {++ "name": "reference",++ "in": "query",++ "required": true,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference; it is the idempotency key."++ },++ "description": "The app's own reference; it is the idempotency key."++ },++ {++ "name": "return_url",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "format": "uri",++ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."++ },++ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.vibes-charge.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/vibes/charges/{id}/refund": {++ "post": {++ "operationId": "post_api_v1_vibes_charges_id_refund",++ "summary": "Sell something for Vibes as a developer app (Vibes Pay, D12).",++ "description": "**billing.vibes.charge** (public): Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",++ "tags": [++ "billing.vibes"++ ],++ "x-openvibe-capabilities": [++ "billing.vibes.charge"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "billing-vibes-write",++ "security": [++ {++ "openvibeToken": [++ "billing.vibes.charge"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.vibes-charge-create-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.vibes-charge.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/vibes/me": {++ "get": {++ "operationId": "get_api_v1_vibes_me",++ "summary": "Read a person's own Vibes as an app the person approved (D12): GET /api/v1/vibes/me with a person-delegated app token answers billing.vibes-wallet@1 — the Vibes to spend, the creator payable that can ",++ "description": "**billing.vibes.read** (public): Read a person's own Vibes as an app the person approved (D12): GET /api/v1/vibes/me with a person-delegated app token answers billing.vibes-wallet@1 — the Vibes to spend, the creator payable that can be cashed out (never into Balance) and today's bits_per_usd. The app sees only the person who approved it, never another person's Vibes.",++ "tags": [++ "billing.vibes"++ ],++ "x-openvibe-capabilities": [++ "billing.vibes.read"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "billing-vibes-read",++ "security": [++ {++ "openvibeToken": [++ "billing.vibes.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.vibes-wallet.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/internal/config": {"get": {"operationId": "get_internal_config",@@@ -3228,17 +3501,8 @@},"billing.balance.v1": {"title": "BillingBalance",-- "description": "billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.",++ "description": "billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.","type": "object",-- "required": [-- "subject",-- "currency",-- "credit",-- "payable",-- "pending_payouts",-- "payable_value_cents",-- "bits_per_usd"-- ],"properties": {"subject": {"$ref": "#/components/schemas/identity.subject-ref.v1"@@@ -3262,7 +3526,122 @@"bits_per_usd": {"type": "number"}-- }++ },++ "$defs": {++ "money-nano-usd": {++ "type": "string",++ "pattern": "^[0-9]{1,19}$",++ "description": "Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float."++ },++ "balance-owner": {++ "oneOf": [++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "A person: usd_credit owned by user:<usr_id>."++ },++ {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "project"++ },++ "id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "A project: usd_credit owned by project:<prj_id>."++ }++ ]++ }++ },++ "oneOf": [++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "owner",++ "balance_nano_usd",++ "held_nano_usd",++ "available_nano_usd"++ ],++ "properties": {++ "owner": {++ "$ref": "#/components/schemas/billing.balance.v1/$defs/balance-owner"++ },++ "balance_nano_usd": {++ "$ref": "#/components/schemas/billing.balance.v1/$defs/money-nano-usd"++ },++ "held_nano_usd": {++ "$ref": "#/components/schemas/billing.balance.v1/$defs/money-nano-usd"++ },++ "available_nano_usd": {++ "$ref": "#/components/schemas/billing.balance.v1/$defs/money-nano-usd"++ },++ "sandbox": {++ "type": "boolean",++ "default": false,++ "description": "True for a sandbox account (D10): sandbox credit only, never real money."++ }++ }++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "subject",++ "currency",++ "credit",++ "payable",++ "pending_payouts",++ "payable_value_cents",++ "bits_per_usd"++ ],++ "properties": {++ "subject": {++ "$ref": "#/components/schemas/identity.subject-ref.v1"++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer"++ },++ "payable": {++ "type": "integer"++ },++ "pending_payouts": {++ "type": "integer"++ },++ "payable_value_cents": {++ "type": "integer",++ "description": "What payable is worth in US cents at today's rate."++ },++ "bits_per_usd": {++ "type": "number"++ }++ }++ }++ ]},"billing.cashout-decision.v1": {"title": "BillingCashoutDecision",@@@ -3534,17 +3913,45 @@},"billing.credit-hold-create-request.v1": {"title": "BillingCreditHoldCreateRequest",-- "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.",++ "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.","type": "object","required": ["idempotency_key","subject",-- "project_id","max_nano_usd","rate_version","request_fingerprint"],"additionalProperties": false,++ "allOf": [++ {++ "if": {++ "type": "object",++ "properties": {++ "subject": {++ "type": "object",++ "properties": {++ "type": {++ "const": "app"++ }++ },++ "required": [++ "type"++ ]++ }++ },++ "required": [++ "subject"++ ]++ },++ "then": {++ "type": "object",++ "required": [++ "project_id"++ ]++ }++ }++ ],"$defs": {"money-nano-usd": {"type": "string",@@@ -3562,7 +3969,8 @@},"project_id": {"type": "string",-- "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app."},"credential_id": {"type": [@@@ -4985,6 +5393,198 @@}}},++ "billing.vibes-charge-create-request.v1": {++ "title": "BillingVibesChargeCreateRequest",++ "description": "billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "amount",++ "description",++ "reference"++ ],++ "properties": {++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference; it is the idempotency key."++ },++ "return_url": {++ "type": "string",++ "format": "uri",++ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."++ }++ }++ },++ "billing.vibes-charge.v1": {++ "title": "BillingVibesCharge",++ "description": "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.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "app",++ "project_id",++ "amount",++ "description",++ "reference",++ "status",++ "pay_url",++ "return_url",++ "created_at",++ "paid_at",++ "refunded_at",++ "expires_at",++ "sandbox"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "ULID charge id."++ },++ "app": {++ "type": "string",++ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app that created the charge (its app_ id)."++ },++ "project_id": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The app's project; paid Vibes land in that project's Vibes wallet."++ },++ "amount": {++ "type": "integer",++ "minimum": 1,++ "description": "Price in whole Vibes. A positive integer; Vibes have no fractions."++ },++ "description": {++ "type": "string",++ "minLength": 1,++ "maxLength": 140,++ "description": "What the person is paying for."++ },++ "reference": {++ "type": "string",++ "minLength": 1,++ "maxLength": 128,++ "description": "The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge."++ },++ "status": {++ "enum": [++ "pending",++ "paid",++ "refunded",++ "expired",++ "cancelled"++ ]++ },++ "pay_url": {++ "type": "string",++ "format": "uri",++ "description": "OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_..."++ },++ "return_url": {++ "type": [++ "string",++ "null"++ ],++ "format": "uri",++ "description": "Where the browser returns after paying; null when the app gave no return URL."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "paid_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When the Vibes moved, or null while the charge is unpaid."++ },++ "refunded_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time",++ "description": "When the Vibes went back to the person, or null."++ },++ "expires_at": {++ "type": "string",++ "format": "date-time",++ "description": "The pay sheet stops accepting payment after this instant."++ },++ "sandbox": {++ "type": "boolean",++ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."++ }++ }++ },++ "billing.vibes-wallet.v1": {++ "title": "BillingVibesWallet",++ "description": "billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "subject",++ "currency",++ "credit",++ "payable",++ "bits_per_usd"++ ],++ "properties": {++ "subject": {++ "type": "object",++ "required": [++ "type",++ "id"++ ],++ "additionalProperties": false,++ "properties": {++ "type": {++ "const": "user"++ },++ "id": {++ "type": "string",++ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"++ }++ },++ "description": "The person the app reads for (the approved delegation). Only a user, never an app or a service."++ },++ "currency": {++ "const": "vibes-bits"++ },++ "credit": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes to spend."++ },++ "payable": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes received as a creator, to cash out; never Balance."++ },++ "bits_per_usd": {++ "type": "number",++ "description": "Today's Vibes per USD rate, the same one a pack is priced with."++ }++ }++ },"common.config-change.v1": {"title": "ConfigChange","description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Codes API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Domains API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Food API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -26,8 +26,8 @@"service": "billing","name": "OpenVibe.Billing","origin": "https://billing.openvibe.network",-- "operations": 43,-- "capabilities": 17++ "operations": 47,++ "capabilities": 19},{"service": "blog",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.MediaHub API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRestream API",-- "version": "0.288.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Play API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Quest API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Report API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Today API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Vision API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Work API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Zone API",-- "version": "0.288.0",-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.289.0",++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -164241,24 +164241,309 @@ export type BillingBalanceReadResult =transaction: BillingTransaction;reversed_by: string[];};--/** [email protected] (owner: billing) *//**-- * billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.++ * billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.*/--export interface BillingBalance {-- subject: SubjectRef;++export type BillingBalance = {++ subject?: SubjectRef;++ currency?: "vibes-bits";++ credit?: number;++ payable?: number;++ pending_payouts?: number;++ /**++ * What payable is worth in US cents at today's rate.++ */++ payable_value_cents?: number;++ bits_per_usd?: number;++} & (++ | {++ owner:++ | {++ type: "user";++ id: string;++ }++ | {++ type: "project";++ id: string;++ };++ /**++ * Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float.++ */++ balance_nano_usd: string;++ /**++ * Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float.++ */++ held_nano_usd: string;++ /**++ * Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float.++ */++ available_nano_usd: string;++ /**++ * True for a sandbox account (D10): sandbox credit only, never real money.++ */++ sandbox?: boolean;++ }++ | {++ subject: SubjectRef;++ currency: "vibes-bits";++ credit: number;++ payable: number;++ pending_payouts: number;++ /**++ * What payable is worth in US cents at today's rate.++ */++ payable_value_cents: number;++ bits_per_usd: number;++ }++);++++/** [email protected] (owner: billing) */++/**++ * 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.++ */++export interface BillingVibesCharge {++ /**++ * ULID charge id.++ */++ id: string;++ /**++ * The app that created the charge (its app_ id).++ */++ app: string;++ /**++ * The app's project; paid Vibes land in that project's Vibes wallet.++ */++ project_id: string;++ /**++ * Price in whole Vibes. A positive integer; Vibes have no fractions.++ */++ amount: number;++ /**++ * What the person is paying for.++ */++ description: string;++ /**++ * The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge.++ */++ reference: string;++ status: "pending" | "paid" | "refunded" | "expired" | "cancelled";++ /**++ * OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_...++ */++ pay_url: string;++ /**++ * Where the browser returns after paying; null when the app gave no return URL.++ */++ return_url: string | null;++ created_at: string;++ /**++ * When the Vibes moved, or null while the charge is unpaid.++ */++ paid_at: string | null;++ /**++ * When the Vibes went back to the person, or null.++ */++ refunded_at: string | null;++ /**++ * The pay sheet stops accepting payment after this instant.++ */++ expires_at: string;++ /**++ * True for a sandbox app: sandbox Vibes only, worth nothing.++ */++ sandbox: boolean;++}++++/** [email protected] (owner: billing) */++/**++ * billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.++ */++export interface BillingVibesChargeCreateRequest {++ /**++ * Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount.++ */++ amount: number;++ /**++ * What the person is paying for.++ */++ description: string;++ /**++ * The app's own reference; it is the idempotency key.++ */++ reference: string;++ /**++ * Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook.++ */++ return_url?: string;++}++++/** [email protected] (owner: billing) */++/**++ * billing.vibes-charge-refund-request@1: the optional body of POST /api/v1/vibes/charges/:id/refund. A refund is always for the whole charge: the Vibes go back from the project's Vibes wallet to the person. reason is recorded on the refund. No amount may be given, because a partial refund is not a Vibes Pay refund.++ */++export interface BillingVibesChargeRefundRequest {++ /**++ * Why the app refunded, for its own and the person's record.++ */++ reason?: string | null;++}++++/** [email protected] (owner: billing) */++/**++ * billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.++ */++export interface BillingVibesWallet {++ /**++ * The person the app reads for (the approved delegation). Only a user, never an app or a service.++ */++ subject: {++ type: "user";++ id: string;++ };currency: "vibes-bits";++ /**++ * Vibes to spend.++ */credit: number;++ /**++ * Vibes received as a creator, to cash out; never Balance.++ */payable: number;-- pending_payouts: number;/**-- * What payable is worth in US cents at today's rate.++ * Today's Vibes per USD rate, the same one a pack is priced with.*/-- payable_value_cents: number;bits_per_usd: number;}++/** [email protected] (owner: billing) */++/**++ * billing.balance-transfer-request@1: body of POST /api/me/balance/transfers (D12). Moves amount_nano_usd from the signed-in person's Balance to a project they administer, into that project's Balance. Money is positive integer nano-USD as a decimal string (D1), never a float; the transfer is refused when the Balance or the person's project role is short. idempotency_key makes the transfer exactly once: a replay with the same key and body returns the first answer and moves nothing.++ */++export interface BillingBalanceTransferRequest {++ /**++ * The project whose Balance receives the money; the person must administer it.++ */++ to_project_id: string;++ /**++ * A positive integer nano-USD (1e-9 USD) as a decimal string (D1), never a float.++ */++ amount_nano_usd: string;++ /**++ * Unique per mutation. A replay with the same key and body returns the first answer and changes nothing.++ */++ idempotency_key: string;++}++++/** [email protected] (owner: billing) */++/**++ * billing.vibes-purchase-from-balance-request@1: body of POST /api/me/vibes/from-balance (D12). Buys a whole number of Vibes with the signed-in person's Balance at the same Vibes price as a pack, charging exact nano-USD; Vibes -> Balance is never allowed. vibes is a whole integer (Vibes have no fractions); the money side is integer nano-USD (D1), never a float. idempotency_key makes the purchase exactly once.++ */++export interface BillingVibesPurchaseFromBalanceRequest {++ /**++ * How many whole Vibes to buy.++ */++ vibes: number;++ /**++ * Unique per mutation. A replay with the same key and body returns the first answer and changes nothing.++ */++ idempotency_key: string;++}++++/** [email protected] (owner: billing) */++/**++ * billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.++ */++export interface BillingVibesChargePaidPayload {++ /**++ * The charge; GET /api/v1/vibes/charges/:id returns its current state.++ */++ id: string;++ /**++ * The app that created the charge.++ */++ app: string;++ /**++ * The app's project, whose Vibes wallet received the charge.++ */++ project_id: string;++ /**++ * The whole number of Vibes paid.++ */++ amount: number;++ description: string;++ /**++ * The app's reference, as given when the charge was created.++ */++ reference: string;++ status: "paid";++ created_at: string;++ /**++ * When the Vibes moved from the person to the project.++ */++ paid_at: string;++ /**++ * Null: a paid charge has not been refunded.++ */++ refunded_at: null;++ expires_at: string;++ /**++ * True for a sandbox app: sandbox Vibes only, worth nothing.++ */++ sandbox: boolean;++ /**++ * Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer.++ */++ payer_ref: string;++}++++/** [email protected] (owner: billing) */++/**++ * billing.vibes.charge.refunded v1 (OpenVibe.Billing, written when a Vibes Pay charge is refunded): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.++ */++export interface BillingVibesChargeRefundedPayload {++ /**++ * The charge; GET /api/v1/vibes/charges/:id returns its current state.++ */++ id: string;++ /**++ * The app that created the charge.++ */++ app: string;++ /**++ * The app's project, whose Vibes wallet received the charge.++ */++ project_id: string;++ /**++ * The whole number of Vibes paid.++ */++ amount: number;++ description: string;++ /**++ * The app's reference, as given when the charge was created.++ */++ reference: string;++ status: "refunded";++ created_at: string;++ /**++ * When the Vibes moved from the person to the project.++ */++ paid_at: string;++ /**++ * When the Vibes went back to the person.++ */++ refunded_at: string;++ expires_at: string;++ /**++ * True for a sandbox app: sandbox Vibes only, worth nothing.++ */++ sandbox: boolean;++ /**++ * Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer.++ */++ payer_ref: string;++}++/** [email protected] (owner: billing) *//*** billing.cashout-decision@1: bodies of billing.cashout.manage on OpenVibe.Billing (staff). POST /api/v1/cashouts/:id/approve: { payout_reference, payout_provider? } after the payout was sent (payout_provider defaults to paypal; refused while the cashout is in escrow). POST /api/v1/cashouts/:id/deny: { reason? } returns the bits to the person's payable. GET /api/v1/cashouts takes no body. Every mutating call needs an Idempotency-Key header (a replay with the same key and body returns the first answer) and is refused with 503 billing.frozen while the economy is frozen. Refusals are problem+json (billing.invalid_input, billing.invalid_amount, billing.insufficient_funds, …).@@@ -164902,12 +165187,17 @@ export interface BillingCreditSettlement {/** [email protected] (owner: billing) *//**-- * billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.++ * billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.*/--export interface BillingCreditHoldCreateRequest {++export type BillingCreditHoldCreateRequest = {++ [k: string]: unknown | undefined;++} & {idempotency_key: string;subject: SubjectRef;-- project_id: string;++ /**++ * The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app.++ */++ project_id?: string;credential_id?: string | null;/*** Integer nano-USD (1e-9 USD), stored as a string.@@@ -164923,7 +165213,7 @@ export interface BillingCreditHoldCreateRequest {* 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.*/sandbox?: boolean;--}++};/** [email protected] (owner: billing) *//**nnew file mode 100644@@@ -0,0 +1,27 @@++{++ "id": "billing.vibes.charge",++ "version": "1.0",++ "owner": "billing",++ "status": "active",++ "visibility": "public",++ "sensitive": true,++ "description": "Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",++ "permissions": [++ "billing:vibes:charge"++ ],++ "resourceConstraints": [++ "project"++ ],++ "quotaClass": "billing-vibes-write",++ "events": [++ "billing.vibes.charge.paid",++ "billing.vibes.charge.refunded"++ ],++ "implementedBy": [++ "POST /api/v1/vibes/charges",++ "GET /api/v1/vibes/charges/:id",++ "POST /api/v1/vibes/charges/:id/refund"++ ],++ "inputSchema": "billing.vibes-charge-create-request@1",++ "outputSchema": "billing.vibes-charge@1"++}nnew file mode 100644@@@ -0,0 +1,21 @@++{++ "id": "billing.vibes.read",++ "version": "1.0",++ "owner": "billing",++ "status": "active",++ "visibility": "public",++ "description": "Read a person's own Vibes as an app the person approved (D12): GET /api/v1/vibes/me with a person-delegated app token answers billing.vibes-wallet@1 — the Vibes to spend, the creator payable that can be cashed out (never into Balance) and today's bits_per_usd. The app sees only the person who approved it, never another person's Vibes.",++ "permissions": [++ "billing:vibes:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "billing-vibes-read",++ "events": [],++ "implementedBy": [++ "GET /api/v1/vibes/me"++ ],++ "inputSchema": "common.no-body@1",++ "outputSchema": "billing.vibes-wallet@1"++}@@@ -36,7 +36,9 @@"billing.credit.hold.settle","billing.credit.hold.release","billing.credit.sandbox.grant",-- "billing.config.manage"++ "billing.config.manage",++ "billing.vibes.charge",++ "billing.vibes.read"],"eventsProduced": ["billing.transaction.settled",@@@ -48,7 +50,9 @@"billing.cashout.denied","billing.staff.action","billing.receipt.external",-- "billing.credit.settled"++ "billing.credit.settled",++ "billing.vibes.charge.paid",++ "billing.vibes.charge.refunded"],"eventsConsumed": [],"namespacesOwned": [@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.288.0",++ "version": "0.289.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",
@@ -1,6 +1,39 @@
@@ -1,6 +1,39 @@
# Changelog
# Changelog
+## 0.289.0 — 2026-11-04
+
+- **Balance and Vibes Pay (AI Exchange D12, MONEY.md build order step 1).**
+ - **Vibes Pay schemas:** `billing.vibes-charge@1` (id `vch_<ULID>`, app, project_id, `amount` in whole Vibes,
+ a 1–140 description, the app's reference, status pending|paid|refunded|expired|cancelled, pay_url, return_url,
+ the timestamps and sandbox), `billing.vibes-charge-create-request@1` (`amount`, `description`, `reference` — the
+ app's idempotency key — and an optional `return_url`) and `billing.vibes-charge-refund-request@1` (an optional
+ `reason`; a refund is always for the whole charge).
+ - **Vibes Pay events:** `billing.vibes.charge.paid@1` and `billing.vibes.charge.refunded@1` are also the signed
+ webhook bodies (HMAC-SHA256, retried with backoff): the charge fields plus an opaque `payer_ref` that is stable
+ per app, and deliberately no person id and no email. Billing's manifest lists both in `eventsProduced`.
+ - **Balance:** `billing.balance@1` gains the D12 Balance branch — `owner` (user or project), `balance_nano_usd`,
+ `held_nano_usd`, `available_nano_usd` and an optional `sandbox`, with money as integer nano-USD decimal strings
+ (D1, never a float) — answered by `GET /api/me/balance`. The contract id is permanent, so the Vibes wallet body
+ that `billing.balance.read` already answered stays as the second branch; both branches are closed with
+ `additionalProperties: false`, and new code sends the Balance branch.
+ - **Balance requests:** `billing.balance-transfer-request@1` (`to_project_id`, positive `amount_nano_usd`,
+ `idempotency_key`) for moving a person's Balance to a project's, and
+ `billing.vibes-purchase-from-balance-request@1` (`vibes` as a whole positive integer, `idempotency_key`) for
+ Get Vibes with Balance.
+ - **Vibes read:** `billing.vibes-wallet@1` is the person's own Vibes — credit, creator payable and bits_per_usd,
+ never a Balance — answered by `GET /api/v1/vibes/me`.
+ - **Capabilities:** `billing.vibes.charge` (public, grantable to apps, a sandbox app included, which charges
+ sandbox Vibes only) implemented by `POST /api/v1/vibes/charges`, `GET /api/v1/vibes/charges/:id` and
+ `POST /api/v1/vibes/charges/:id/refund`; `billing.vibes.read` (public, a person-delegated read of their own
+ Vibes for an app they approved) implemented by `GET /api/v1/vibes/me`.
+ - **Personal holds:** `billing.credit-hold-create-request@1` no longer requires `project_id`; without it the hold
+ is personal and spends the subject's own Balance. An `app` subject must still name its project (`if`/`then`),
+ so the accepted set only grows and the compat gate stays green.
+ - Valid and invalid fixtures cover both Balance branches, whole-number Vibes, D1 decimal-string money, a personal
+ hold, an app subject without a project, a partial-refund body, and webhook payloads with a person id or an
+ email-shaped payer reference.
+
## 0.288.0 — 2026-11-04
## 0.288.0 — 2026-11-04
- **Configuration contracts (AI Exchange D11, S1), built on openvibe-shared/config:
- **Configuration contracts (AI Exchange D11, S1), built on openvibe-shared/config:
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
> Machine-readable contracts for the whole OpenVibe network.
> Machine-readable contracts for the whole OpenVibe network.
-**Status:** alpha (first released in Wave 1; the release is package.json's `version`, tagged). At v0.288.0: 978 schemas in `contracts/catalog.json`, 60 service manifests, 450 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin). `node scripts/docs-currency.js <root>` checks every checkout's README and `STATUS.json` against its code: the pins and versions they name, a library's consumer list, the deployed flag against the manifest, README links, and the paths and migrations they name. `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).
+**Status:** alpha (first released in Wave 1; the release is package.json's `version`, tagged). At v0.289.0: 986 schemas in `contracts/catalog.json`, 60 service manifests, 452 capability manifests, 13 namespace manifests and the staff capability map. Service repositories run `openvibe-contracts-check` and the pin-drift check in CI, and deployed services verify service and app tokens with `serviceAuth` in production. Consumers pin release tags (each repository's `STATUS.json` names its pin). `node scripts/docs-currency.js <root>` checks every checkout's README and `STATUS.json` against its code: the pins and versions they name, a library's consumer list, the deployed flag against the manifest, README links, and the paths and migrations they name. `node scripts/estate.js` generates `docs/ESTATE.md` and `manifests/repositories/` from the estate checkouts, and `--check` fails on drift (plan T1 step 3).
**Plan:** OpenVibe Development Roadmap, Wave 1 (implementation plan rev 3, §3.1 and §18.11).
**Plan:** OpenVibe Development Roadmap, Wave 1 (implementation plan rev 3, §3.1 and §18.11).
**License:** AGPL-3.0 (same as every OpenVibe service).
**License:** AGPL-3.0 (same as every OpenVibe service).
new file mode 100644
new file mode 100644
@@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/balance-transfer-request.v1.json",
+ "title": "BillingBalanceTransferRequest",
+ "description": "billing.balance-transfer-request@1: body of POST /api/me/balance/transfers (D12). Moves amount_nano_usd from the signed-in person's Balance to a project they administer, into that project's Balance. Money is positive integer nano-USD as a decimal string (D1), never a float; the transfer is refused when the Balance or the person's project role is short. idempotency_key makes the transfer exactly once: a replay with the same key and body returns the first answer and moves nothing.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "to_project_id",
+ "amount_nano_usd",
+ "idempotency_key"
+ ],
+ "properties": {
+ "to_project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The project whose Balance receives the money; the person must administer it."
+ },
+ "amount_nano_usd": {
+ "type": "string",
+ "pattern": "^[1-9][0-9]{0,18}$",
+ "description": "A positive integer nano-USD (1e-9 USD) as a decimal string (D1), never a float."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9._:-]{8,128}$",
+ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."
+ }
+ }
+}
@@ -2,16 +2,145 @@
@@ -2,16 +2,145 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/billing/balance.v1.json",
"$id": "https://openvibe.network/contracts/billing/balance.v1.json",
"title": "BillingBalance",
"title": "BillingBalance",
- "description": "billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.",
+ "description": "billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.",
"type": "object",
"type": "object",
- "required": ["subject", "currency", "credit", "payable", "pending_payouts", "payable_value_cents", "bits_per_usd"],
"properties": {
"properties": {
- "subject": { "$ref": "../identity/subject-ref.v1.json" },
- "currency": { "const": "vibes-bits" },
- "credit": { "type": "integer" },
- "payable": { "type": "integer" },
- "pending_payouts": { "type": "integer" },
- "payable_value_cents": { "type": "integer", "description": "What payable is worth in US cents at today's rate." },
- "bits_per_usd": { "type": "number" }
- }
+ "subject": {
+ "$ref": "../identity/subject-ref.v1.json"
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer"
+ },
+ "payable": {
+ "type": "integer"
+ },
+ "pending_payouts": {
+ "type": "integer"
+ },
+ "payable_value_cents": {
+ "type": "integer",
+ "description": "What payable is worth in US cents at today's rate."
+ },
+ "bits_per_usd": {
+ "type": "number"
+ }
+ },
+ "$defs": {
+ "money-nano-usd": {
+ "type": "string",
+ "pattern": "^[0-9]{1,19}$",
+ "description": "Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float."
+ },
+ "balance-owner": {
+ "oneOf": [
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "A person: usd_credit owned by user:<usr_id>."
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "project"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "A project: usd_credit owned by project:<prj_id>."
+ }
+ ]
+ }
+ },
+ "oneOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "owner",
+ "balance_nano_usd",
+ "held_nano_usd",
+ "available_nano_usd"
+ ],
+ "properties": {
+ "owner": {
+ "$ref": "#/$defs/balance-owner"
+ },
+ "balance_nano_usd": {
+ "$ref": "#/$defs/money-nano-usd"
+ },
+ "held_nano_usd": {
+ "$ref": "#/$defs/money-nano-usd"
+ },
+ "available_nano_usd": {
+ "$ref": "#/$defs/money-nano-usd"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "default": false,
+ "description": "True for a sandbox account (D10): sandbox credit only, never real money."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "subject",
+ "currency",
+ "credit",
+ "payable",
+ "pending_payouts",
+ "payable_value_cents",
+ "bits_per_usd"
+ ],
+ "properties": {
+ "subject": {
+ "$ref": "../identity/subject-ref.v1.json"
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer"
+ },
+ "payable": {
+ "type": "integer"
+ },
+ "pending_payouts": {
+ "type": "integer"
+ },
+ "payable_value_cents": {
+ "type": "integer",
+ "description": "What payable is worth in US cents at today's rate."
+ },
+ "bits_per_usd": {
+ "type": "number"
+ }
+ }
+ }
+ ]
}
}
@@ -2,17 +2,45 @@
@@ -2,17 +2,45 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/billing/credit-hold-create-request.v1.json",
"$id": "https://openvibe.network/contracts/billing/credit-hold-create-request.v1.json",
"title": "BillingCreditHoldCreateRequest",
"title": "BillingCreditHoldCreateRequest",
- "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.",
+ "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.",
"type": "object",
"type": "object",
"required": [
"required": [
"idempotency_key",
"idempotency_key",
"subject",
"subject",
- "project_id",
"max_nano_usd",
"max_nano_usd",
"rate_version",
"rate_version",
"request_fingerprint"
"request_fingerprint"
],
],
"additionalProperties": false,
"additionalProperties": false,
+ "allOf": [
+ {
+ "if": {
+ "type": "object",
+ "properties": {
+ "subject": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "const": "app"
+ }
+ },
+ "required": [
+ "type"
+ ]
+ }
+ },
+ "required": [
+ "subject"
+ ]
+ },
+ "then": {
+ "type": "object",
+ "required": [
+ "project_id"
+ ]
+ }
+ }
+ ],
"$defs": {
"$defs": {
"money-nano-usd": {
"money-nano-usd": {
"type": "string",
"type": "string",
@@ -30,7 +58,8 @@
@@ -30,7 +58,8 @@
},
},
"project_id": {
"project_id": {
"type": "string",
"type": "string",
- "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app."
},
},
"credential_id": {
"credential_id": {
"type": [
"type": [
new file mode 100644
new file mode 100644
@@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-charge-create-request.v1.json",
+ "title": "BillingVibesChargeCreateRequest",
+ "description": "billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "amount",
+ "description",
+ "reference"
+ ],
+ "properties": {
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference; it is the idempotency key."
+ },
+ "return_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-charge-refund-request.v1.json",
+ "title": "BillingVibesChargeRefundRequest",
+ "description": "billing.vibes-charge-refund-request@1: the optional body of POST /api/v1/vibes/charges/:id/refund. A refund is always for the whole charge: the Vibes go back from the project's Vibes wallet to the person. reason is recorded on the refund. No amount may be given, because a partial refund is not a Vibes Pay refund.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 300,
+ "description": "Why the app refunded, for its own and the person's record."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,109 @@
@@ -0,0 +1,109 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-charge.v1.json",
+ "title": "BillingVibesCharge",
+ "description": "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.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "pay_url",
+ "return_url",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "ULID charge id."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge (its app_ id)."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project; paid Vibes land in that project's Vibes wallet."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes. A positive integer; Vibes have no fractions."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge."
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "paid",
+ "refunded",
+ "expired",
+ "cancelled"
+ ]
+ },
+ "pay_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_..."
+ },
+ "return_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri",
+ "description": "Where the browser returns after paying; null when the app gave no return URL."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When the Vibes moved, or null while the charge is unpaid."
+ },
+ "refunded_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When the Vibes went back to the person, or null."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "The pay sheet stops accepting payment after this instant."
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-purchase-from-balance-request.v1.json",
+ "title": "BillingVibesPurchaseFromBalanceRequest",
+ "description": "billing.vibes-purchase-from-balance-request@1: body of POST /api/me/vibes/from-balance (D12). Buys a whole number of Vibes with the signed-in person's Balance at the same Vibes price as a pack, charging exact nano-USD; Vibes -> Balance is never allowed. vibes is a whole integer (Vibes have no fractions); the money side is integer nano-USD (D1), never a float. idempotency_key makes the purchase exactly once.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "vibes",
+ "idempotency_key"
+ ],
+ "properties": {
+ "vibes": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "How many whole Vibes to buy."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9._:-]{8,128}$",
+ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-wallet.v1.json",
+ "title": "BillingVibesWallet",
+ "description": "billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "subject",
+ "currency",
+ "credit",
+ "payable",
+ "bits_per_usd"
+ ],
+ "properties": {
+ "subject": {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "The person the app reads for (the approved delegation). Only a user, never an app or a service."
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes to spend."
+ },
+ "payable": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes received as a creator, to cash out; never Balance."
+ },
+ "bits_per_usd": {
+ "type": "number",
+ "description": "Today's Vibes per USD rate, the same one a pack is priced with."
+ }
+ }
+}
@@ -2295,6 +2295,78 @@
@@ -2295,6 +2295,78 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "billing.vibes-charge",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/vibes-charge.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.vibes-charge-create-request",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/vibes-charge-create-request.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.vibes-charge-refund-request",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/vibes-charge-refund-request.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.vibes-wallet",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/vibes-wallet.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.balance-transfer-request",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/balance-transfer-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.vibes-purchase-from-balance-request",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/vibes-purchase-from-balance-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.vibes.charge.paid",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "events/payloads/billing.vibes.charge.paid.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "billing.vibes.charge.refunded",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "events/payloads/billing.vibes.charge.refunded.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "billing.cashout-decision",
"id": "billing.cashout-decision",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.paid.v1.json",
+ "title": "BillingVibesChargePaidPayload",
+ "description": "billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox",
+ "payer_ref"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project, whose Vibes wallet received the charge."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The whole number of Vibes paid."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's reference, as given when the charge was created."
+ },
+ "status": {
+ "const": "paid"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the Vibes moved from the person to the project."
+ },
+ "refunded_at": {
+ "type": "null",
+ "description": "Null: a paid charge has not been refunded."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ },
+ "payer_ref": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 128,
+ "pattern": "^[A-Za-z0-9._:-]+$",
+ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.refunded.v1.json",
+ "title": "BillingVibesChargeRefundedPayload",
+ "description": "billing.vibes.charge.refunded v1 (OpenVibe.Billing, written when a Vibes Pay charge is refunded): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox",
+ "payer_ref"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project, whose Vibes wallet received the charge."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The whole number of Vibes paid."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's reference, as given when the charge was created."
+ },
+ "status": {
+ "const": "refunded"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the Vibes moved from the person to the project."
+ },
+ "refunded_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the Vibes went back to the person."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ },
+ "payer_ref": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 128,
+ "pattern": "^[A-Za-z0-9._:-]+$",
+ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount_nano_usd": "5000000000",
+ "idempotency_key": "transfer-key-001",
+ "to_user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "amount_nano_usd": "5000000000",
+ "idempotency_key": "transfer-key-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount_nano_usd": 5000000000,
+ "idempotency_key": "transfer-key-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount_nano_usd": "0",
+ "idempotency_key": "transfer-key-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "to_project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount_nano_usd": "5000000000",
+ "idempotency_key": "transfer-key-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "1",
+ "held_nano_usd": "0",
+ "available_nano_usd": "1",
+ "promo_bits": 5
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
+{
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "12400000000",
+ "held_nano_usd": "0"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "1",
+ "held_nano_usd": "0",
+ "available_nano_usd": "1",
+ "subject": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "currency": "vibes-bits",
+ "credit": 1,
+ "payable": 0,
+ "pending_payouts": 0,
+ "payable_value_cents": 0,
+ "bits_per_usd": 100
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": 12400000000,
+ "held_nano_usd": "0",
+ "available_nano_usd": "12400000000"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "owner": {
+ "type": "guest",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "1",
+ "held_nano_usd": "0",
+ "available_nano_usd": "1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "owner": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "12400000000",
+ "held_nano_usd": "0",
+ "available_nano_usd": "12400000000",
+ "sandbox": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "owner": {
+ "type": "project",
+ "id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "5000000000",
+ "held_nano_usd": "1200000000",
+ "available_nano_usd": "3800000000"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "owner": {
+ "type": "project",
+ "id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "balance_nano_usd": "100000000",
+ "held_nano_usd": "0",
+ "available_nano_usd": "100000000",
+ "sandbox": true
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "idempotency_key": "hold-key-app-001",
+ "subject": {
+ "type": "app",
+ "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "max_nano_usd": "50000000",
+ "rate_version": "rr-2026-11-03-001",
+ "request_fingerprint": "fp-chat-app-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "idempotency_key": "hold-key-personal-001",
+ "subject": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "max_nano_usd": "100000000",
+ "rate_version": "rr-2026-11-03-001",
+ "request_fingerprint": "fp-chat-personal-001",
+ "service": "ai"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "amount": 50,
+ "description": "",
+ "reference": "order_123"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "amount": 0.5,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "amount": 50,
+ "description": "Sticker pack: Neon cats"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "return_url": "not a url"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "amount": 1,
+ "description": "One cheer",
+ "reference": "order_124"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "return_url": "https://myapp.example/thanks"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "amount": 25
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "reason": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "reason": "The sticker pack was removed"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "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"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "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
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "vibes": 12.5,
+ "idempotency_key": "vibes-buy-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "vibes": "1250",
+ "idempotency_key": "vibes-buy-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "vibes": 0,
+ "idempotency_key": "vibes-buy-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "vibes": 1250,
+ "idempotency_key": "vibes-buy-001"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "subject": {
+ "type": "app",
+ "id": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "currency": "vibes-bits",
+ "credit": 0,
+ "payable": 0,
+ "bits_per_usd": 100
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
+{
+ "subject": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "currency": "vibes-bits",
+ "credit": 1250,
+ "payable": 0,
+ "bits_per_usd": 100,
+ "balance_nano_usd": "1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "subject": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "currency": "vibes-bits",
+ "credit": -1,
+ "payable": 0,
+ "bits_per_usd": 100
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "subject": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "currency": "usd-cents",
+ "credit": 1250,
+ "payable": 0,
+ "bits_per_usd": 100
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
+{
+ "subject": {
+ "type": "user",
+ "id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+ },
+ "currency": "vibes-bits",
+ "credit": 1250,
+ "payable": 340,
+ "bits_per_usd": 100
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50.5,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "paid",
+ "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": false,
+ "payer_ref": "payer_7f3c9a12"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "paid",
+ "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": false,
+ "payer_ref": "[email protected]"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "paid",
+ "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": false,
+ "payer_ref": "payer_7f3c9a12",
+ "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "paid",
+ "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": false,
+ "payer_ref": "payer_7f3c9a12"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "paid",
+ "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,
+ "payer_ref": "payer_7f3c9a12"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "refunded",
+ "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,
+ "payer_ref": "payer_7f3c9a12",
+ "user_id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "refunded",
+ "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": false,
+ "payer_ref": "payer_7f3c9a12"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
+{
+ "id": "vch_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "app": "app_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "amount": 50,
+ "description": "Sticker pack: Neon cats",
+ "reference": "order_123",
+ "status": "refunded",
+ "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,
+ "payer_ref": "payer_7f3c9a12"
+}
@@ -26246,17 +26246,8 @@
@@ -26246,17 +26246,8 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/billing/balance.v1.json",
"$id": "https://openvibe.network/contracts/billing/balance.v1.json",
"title": "BillingBalance",
"title": "BillingBalance",
- "description": "billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.",
+ "description": "billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.",
"type": "object",
"type": "object",
- "required": [
- "subject",
- "currency",
- "credit",
- "payable",
- "pending_payouts",
- "payable_value_cents",
- "bits_per_usd"
- ],
"properties": {
"properties": {
"subject": {
"subject": {
"$ref": "../identity/subject-ref.v1.json"
"$ref": "../identity/subject-ref.v1.json"
@@ -26280,6 +26271,606 @@
@@ -26280,6 +26271,606 @@
"bits_per_usd": {
"bits_per_usd": {
"type": "number"
"type": "number"
}
}
+ },
+ "$defs": {
+ "money-nano-usd": {
+ "type": "string",
+ "pattern": "^[0-9]{1,19}$",
+ "description": "Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float."
+ },
+ "balance-owner": {
+ "oneOf": [
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "A person: usd_credit owned by user:<usr_id>."
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "project"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "A project: usd_credit owned by project:<prj_id>."
+ }
+ ]
+ }
+ },
+ "oneOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "owner",
+ "balance_nano_usd",
+ "held_nano_usd",
+ "available_nano_usd"
+ ],
+ "properties": {
+ "owner": {
+ "$ref": "#/$defs/balance-owner"
+ },
+ "balance_nano_usd": {
+ "$ref": "#/$defs/money-nano-usd"
+ },
+ "held_nano_usd": {
+ "$ref": "#/$defs/money-nano-usd"
+ },
+ "available_nano_usd": {
+ "$ref": "#/$defs/money-nano-usd"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "default": false,
+ "description": "True for a sandbox account (D10): sandbox credit only, never real money."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "subject",
+ "currency",
+ "credit",
+ "payable",
+ "pending_payouts",
+ "payable_value_cents",
+ "bits_per_usd"
+ ],
+ "properties": {
+ "subject": {
+ "$ref": "../identity/subject-ref.v1.json"
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer"
+ },
+ "payable": {
+ "type": "integer"
+ },
+ "pending_payouts": {
+ "type": "integer"
+ },
+ "payable_value_cents": {
+ "type": "integer",
+ "description": "What payable is worth in US cents at today's rate."
+ },
+ "bits_per_usd": {
+ "type": "number"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "billing.vibes-charge": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-charge.v1.json",
+ "title": "BillingVibesCharge",
+ "description": "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.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "pay_url",
+ "return_url",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "ULID charge id."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge (its app_ id)."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project; paid Vibes land in that project's Vibes wallet."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes. A positive integer; Vibes have no fractions."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge."
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "paid",
+ "refunded",
+ "expired",
+ "cancelled"
+ ]
+ },
+ "pay_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_..."
+ },
+ "return_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri",
+ "description": "Where the browser returns after paying; null when the app gave no return URL."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When the Vibes moved, or null while the charge is unpaid."
+ },
+ "refunded_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When the Vibes went back to the person, or null."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "The pay sheet stops accepting payment after this instant."
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ }
+ }
+ }
+ },
+ "billing.vibes-charge-create-request": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-charge-create-request.v1.json",
+ "title": "BillingVibesChargeCreateRequest",
+ "description": "billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "amount",
+ "description",
+ "reference"
+ ],
+ "properties": {
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference; it is the idempotency key."
+ },
+ "return_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."
+ }
+ }
+ }
+ },
+ "billing.vibes-charge-refund-request": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-charge-refund-request.v1.json",
+ "title": "BillingVibesChargeRefundRequest",
+ "description": "billing.vibes-charge-refund-request@1: the optional body of POST /api/v1/vibes/charges/:id/refund. A refund is always for the whole charge: the Vibes go back from the project's Vibes wallet to the person. reason is recorded on the refund. No amount may be given, because a partial refund is not a Vibes Pay refund.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 300,
+ "description": "Why the app refunded, for its own and the person's record."
+ }
+ }
+ }
+ },
+ "billing.vibes-wallet": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-wallet.v1.json",
+ "title": "BillingVibesWallet",
+ "description": "billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "subject",
+ "currency",
+ "credit",
+ "payable",
+ "bits_per_usd"
+ ],
+ "properties": {
+ "subject": {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "The person the app reads for (the approved delegation). Only a user, never an app or a service."
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes to spend."
+ },
+ "payable": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes received as a creator, to cash out; never Balance."
+ },
+ "bits_per_usd": {
+ "type": "number",
+ "description": "Today's Vibes per USD rate, the same one a pack is priced with."
+ }
+ }
+ }
+ },
+ "billing.balance-transfer-request": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/balance-transfer-request.v1.json",
+ "title": "BillingBalanceTransferRequest",
+ "description": "billing.balance-transfer-request@1: body of POST /api/me/balance/transfers (D12). Moves amount_nano_usd from the signed-in person's Balance to a project they administer, into that project's Balance. Money is positive integer nano-USD as a decimal string (D1), never a float; the transfer is refused when the Balance or the person's project role is short. idempotency_key makes the transfer exactly once: a replay with the same key and body returns the first answer and moves nothing.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "to_project_id",
+ "amount_nano_usd",
+ "idempotency_key"
+ ],
+ "properties": {
+ "to_project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The project whose Balance receives the money; the person must administer it."
+ },
+ "amount_nano_usd": {
+ "type": "string",
+ "pattern": "^[1-9][0-9]{0,18}$",
+ "description": "A positive integer nano-USD (1e-9 USD) as a decimal string (D1), never a float."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9._:-]{8,128}$",
+ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."
+ }
+ }
+ }
+ },
+ "billing.vibes-purchase-from-balance-request": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/vibes-purchase-from-balance-request.v1.json",
+ "title": "BillingVibesPurchaseFromBalanceRequest",
+ "description": "billing.vibes-purchase-from-balance-request@1: body of POST /api/me/vibes/from-balance (D12). Buys a whole number of Vibes with the signed-in person's Balance at the same Vibes price as a pack, charging exact nano-USD; Vibes -> Balance is never allowed. vibes is a whole integer (Vibes have no fractions); the money side is integer nano-USD (D1), never a float. idempotency_key makes the purchase exactly once.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "vibes",
+ "idempotency_key"
+ ],
+ "properties": {
+ "vibes": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "How many whole Vibes to buy."
+ },
+ "idempotency_key": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9._:-]{8,128}$",
+ "description": "Unique per mutation. A replay with the same key and body returns the first answer and changes nothing."
+ }
+ }
+ }
+ },
+ "billing.vibes.charge.paid": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.paid.v1.json",
+ "title": "BillingVibesChargePaidPayload",
+ "description": "billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox",
+ "payer_ref"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project, whose Vibes wallet received the charge."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The whole number of Vibes paid."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's reference, as given when the charge was created."
+ },
+ "status": {
+ "const": "paid"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the Vibes moved from the person to the project."
+ },
+ "refunded_at": {
+ "type": "null",
+ "description": "Null: a paid charge has not been refunded."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ },
+ "payer_ref": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 128,
+ "pattern": "^[A-Za-z0-9._:-]+$",
+ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."
+ }
+ }
+ }
+ },
+ "billing.vibes.charge.refunded": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/events/payloads/billing.vibes.charge.refunded.v1.json",
+ "title": "BillingVibesChargeRefundedPayload",
+ "description": "billing.vibes.charge.refunded v1 (OpenVibe.Billing, written when a Vibes Pay charge is refunded): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox",
+ "payer_ref"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The charge; GET /api/v1/vibes/charges/:id returns its current state."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project, whose Vibes wallet received the charge."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "The whole number of Vibes paid."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's reference, as given when the charge was created."
+ },
+ "status": {
+ "const": "refunded"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the Vibes moved from the person to the project."
+ },
+ "refunded_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the Vibes went back to the person."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ },
+ "payer_ref": {
+ "type": "string",
+ "minLength": 8,
+ "maxLength": 128,
+ "pattern": "^[A-Za-z0-9._:-]+$",
+ "description": "Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer."
+ }
}
}
}
}
},
},
@@ -28052,17 +28643,45 @@
@@ -28052,17 +28643,45 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/billing/credit-hold-create-request.v1.json",
"$id": "https://openvibe.network/contracts/billing/credit-hold-create-request.v1.json",
"title": "BillingCreditHoldCreateRequest",
"title": "BillingCreditHoldCreateRequest",
- "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.",
+ "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.",
"type": "object",
"type": "object",
"required": [
"required": [
"idempotency_key",
"idempotency_key",
"subject",
"subject",
- "project_id",
"max_nano_usd",
"max_nano_usd",
"rate_version",
"rate_version",
"request_fingerprint"
"request_fingerprint"
],
],
"additionalProperties": false,
"additionalProperties": false,
+ "allOf": [
+ {
+ "if": {
+ "type": "object",
+ "properties": {
+ "subject": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "const": "app"
+ }
+ },
+ "required": [
+ "type"
+ ]
+ }
+ },
+ "required": [
+ "subject"
+ ]
+ },
+ "then": {
+ "type": "object",
+ "required": [
+ "project_id"
+ ]
+ }
+ }
+ ],
"$defs": {
"$defs": {
"money-nano-usd": {
"money-nano-usd": {
"type": "string",
"type": "string",
@@ -28080,7 +28699,8 @@
@@ -28080,7 +28699,8 @@
},
},
"project_id": {
"project_id": {
"type": "string",
"type": "string",
- "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app."
},
},
"credential_id": {
"credential_id": {
"type": [
"type": [
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bench API",
"title": "OpenVibe.Bench API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -1933,6 +1933,279 @@
@@ -1933,6 +1933,279 @@
}
}
}
}
},
},
+ "/api/v1/vibes/charges": {
+ "post": {
+ "operationId": "post_api_v1_vibes_charges",
+ "summary": "Sell something for Vibes as a developer app (Vibes Pay, D12).",
+ "description": "**billing.vibes.charge** (public): Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",
+ "tags": [
+ "billing.vibes"
+ ],
+ "x-openvibe-capabilities": [
+ "billing.vibes.charge"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "billing-vibes-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "billing.vibes.charge"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.vibes-charge-create-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.vibes-charge.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/vibes/charges/{id}": {
+ "get": {
+ "operationId": "get_api_v1_vibes_charges_id",
+ "summary": "Sell something for Vibes as a developer app (Vibes Pay, D12).",
+ "description": "**billing.vibes.charge** (public): Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",
+ "tags": [
+ "billing.vibes"
+ ],
+ "x-openvibe-capabilities": [
+ "billing.vibes.charge"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "billing-vibes-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "billing.vibes.charge"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/billing.vibes-charge-create-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "amount",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."
+ },
+ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."
+ },
+ {
+ "name": "description",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "description": "What the person is paying for."
+ },
+ {
+ "name": "reference",
+ "in": "query",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference; it is the idempotency key."
+ },
+ "description": "The app's own reference; it is the idempotency key."
+ },
+ {
+ "name": "return_url",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "format": "uri",
+ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."
+ },
+ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.vibes-charge.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/vibes/charges/{id}/refund": {
+ "post": {
+ "operationId": "post_api_v1_vibes_charges_id_refund",
+ "summary": "Sell something for Vibes as a developer app (Vibes Pay, D12).",
+ "description": "**billing.vibes.charge** (public): Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",
+ "tags": [
+ "billing.vibes"
+ ],
+ "x-openvibe-capabilities": [
+ "billing.vibes.charge"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "billing-vibes-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "billing.vibes.charge"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.vibes-charge-create-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.vibes-charge.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/vibes/me": {
+ "get": {
+ "operationId": "get_api_v1_vibes_me",
+ "summary": "Read a person's own Vibes as an app the person approved (D12): GET /api/v1/vibes/me with a person-delegated app token answers billing.vibes-wallet@1 — the Vibes to spend, the creator payable that can ",
+ "description": "**billing.vibes.read** (public): Read a person's own Vibes as an app the person approved (D12): GET /api/v1/vibes/me with a person-delegated app token answers billing.vibes-wallet@1 — the Vibes to spend, the creator payable that can be cashed out (never into Balance) and today's bits_per_usd. The app sees only the person who approved it, never another person's Vibes.",
+ "tags": [
+ "billing.vibes"
+ ],
+ "x-openvibe-capabilities": [
+ "billing.vibes.read"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "billing-vibes-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "billing.vibes.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.vibes-wallet.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/internal/config": {
"/internal/config": {
"get": {
"get": {
"operationId": "get_internal_config",
"operationId": "get_internal_config",
@@ -3228,17 +3501,8 @@
@@ -3228,17 +3501,8 @@
},
},
"billing.balance.v1": {
"billing.balance.v1": {
"title": "BillingBalance",
"title": "BillingBalance",
- "description": "billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.",
+ "description": "billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.",
"type": "object",
"type": "object",
- "required": [
- "subject",
- "currency",
- "credit",
- "payable",
- "pending_payouts",
- "payable_value_cents",
- "bits_per_usd"
- ],
"properties": {
"properties": {
"subject": {
"subject": {
"$ref": "#/components/schemas/identity.subject-ref.v1"
"$ref": "#/components/schemas/identity.subject-ref.v1"
@@ -3262,7 +3526,122 @@
@@ -3262,7 +3526,122 @@
"bits_per_usd": {
"bits_per_usd": {
"type": "number"
"type": "number"
}
}
- }
+ },
+ "$defs": {
+ "money-nano-usd": {
+ "type": "string",
+ "pattern": "^[0-9]{1,19}$",
+ "description": "Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float."
+ },
+ "balance-owner": {
+ "oneOf": [
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "A person: usd_credit owned by user:<usr_id>."
+ },
+ {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "project"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "A project: usd_credit owned by project:<prj_id>."
+ }
+ ]
+ }
+ },
+ "oneOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "owner",
+ "balance_nano_usd",
+ "held_nano_usd",
+ "available_nano_usd"
+ ],
+ "properties": {
+ "owner": {
+ "$ref": "#/components/schemas/billing.balance.v1/$defs/balance-owner"
+ },
+ "balance_nano_usd": {
+ "$ref": "#/components/schemas/billing.balance.v1/$defs/money-nano-usd"
+ },
+ "held_nano_usd": {
+ "$ref": "#/components/schemas/billing.balance.v1/$defs/money-nano-usd"
+ },
+ "available_nano_usd": {
+ "$ref": "#/components/schemas/billing.balance.v1/$defs/money-nano-usd"
+ },
+ "sandbox": {
+ "type": "boolean",
+ "default": false,
+ "description": "True for a sandbox account (D10): sandbox credit only, never real money."
+ }
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "subject",
+ "currency",
+ "credit",
+ "payable",
+ "pending_payouts",
+ "payable_value_cents",
+ "bits_per_usd"
+ ],
+ "properties": {
+ "subject": {
+ "$ref": "#/components/schemas/identity.subject-ref.v1"
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer"
+ },
+ "payable": {
+ "type": "integer"
+ },
+ "pending_payouts": {
+ "type": "integer"
+ },
+ "payable_value_cents": {
+ "type": "integer",
+ "description": "What payable is worth in US cents at today's rate."
+ },
+ "bits_per_usd": {
+ "type": "number"
+ }
+ }
+ }
+ ]
},
},
"billing.cashout-decision.v1": {
"billing.cashout-decision.v1": {
"title": "BillingCashoutDecision",
"title": "BillingCashoutDecision",
@@ -3534,17 +3913,45 @@
@@ -3534,17 +3913,45 @@
},
},
"billing.credit-hold-create-request.v1": {
"billing.credit-hold-create-request.v1": {
"title": "BillingCreditHoldCreateRequest",
"title": "BillingCreditHoldCreateRequest",
- "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.",
+ "description": "billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.",
"type": "object",
"type": "object",
"required": [
"required": [
"idempotency_key",
"idempotency_key",
"subject",
"subject",
- "project_id",
"max_nano_usd",
"max_nano_usd",
"rate_version",
"rate_version",
"request_fingerprint"
"request_fingerprint"
],
],
"additionalProperties": false,
"additionalProperties": false,
+ "allOf": [
+ {
+ "if": {
+ "type": "object",
+ "properties": {
+ "subject": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "const": "app"
+ }
+ },
+ "required": [
+ "type"
+ ]
+ }
+ },
+ "required": [
+ "subject"
+ ]
+ },
+ "then": {
+ "type": "object",
+ "required": [
+ "project_id"
+ ]
+ }
+ }
+ ],
"$defs": {
"$defs": {
"money-nano-usd": {
"money-nano-usd": {
"type": "string",
"type": "string",
@@ -3562,7 +3969,8 @@
@@ -3562,7 +3969,8 @@
},
},
"project_id": {
"project_id": {
"type": "string",
"type": "string",
- "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app."
},
},
"credential_id": {
"credential_id": {
"type": [
"type": [
@@ -4985,6 +5393,198 @@
@@ -4985,6 +5393,198 @@
}
}
}
}
},
},
+ "billing.vibes-charge-create-request.v1": {
+ "title": "BillingVibesChargeCreateRequest",
+ "description": "billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "amount",
+ "description",
+ "reference"
+ ],
+ "properties": {
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference; it is the idempotency key."
+ },
+ "return_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook."
+ }
+ }
+ },
+ "billing.vibes-charge.v1": {
+ "title": "BillingVibesCharge",
+ "description": "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.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "app",
+ "project_id",
+ "amount",
+ "description",
+ "reference",
+ "status",
+ "pay_url",
+ "return_url",
+ "created_at",
+ "paid_at",
+ "refunded_at",
+ "expires_at",
+ "sandbox"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^vch_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "ULID charge id."
+ },
+ "app": {
+ "type": "string",
+ "pattern": "^app_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app that created the charge (its app_ id)."
+ },
+ "project_id": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The app's project; paid Vibes land in that project's Vibes wallet."
+ },
+ "amount": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Price in whole Vibes. A positive integer; Vibes have no fractions."
+ },
+ "description": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 140,
+ "description": "What the person is paying for."
+ },
+ "reference": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 128,
+ "description": "The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge."
+ },
+ "status": {
+ "enum": [
+ "pending",
+ "paid",
+ "refunded",
+ "expired",
+ "cancelled"
+ ]
+ },
+ "pay_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_..."
+ },
+ "return_url": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "uri",
+ "description": "Where the browser returns after paying; null when the app gave no return URL."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "paid_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When the Vibes moved, or null while the charge is unpaid."
+ },
+ "refunded_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time",
+ "description": "When the Vibes went back to the person, or null."
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "The pay sheet stops accepting payment after this instant."
+ },
+ "sandbox": {
+ "type": "boolean",
+ "description": "True for a sandbox app: sandbox Vibes only, worth nothing."
+ }
+ }
+ },
+ "billing.vibes-wallet.v1": {
+ "title": "BillingVibesWallet",
+ "description": "billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "subject",
+ "currency",
+ "credit",
+ "payable",
+ "bits_per_usd"
+ ],
+ "properties": {
+ "subject": {
+ "type": "object",
+ "required": [
+ "type",
+ "id"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "const": "user"
+ },
+ "id": {
+ "type": "string",
+ "pattern": "^usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ }
+ },
+ "description": "The person the app reads for (the approved delegation). Only a user, never an app or a service."
+ },
+ "currency": {
+ "const": "vibes-bits"
+ },
+ "credit": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes to spend."
+ },
+ "payable": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes received as a creator, to cash out; never Balance."
+ },
+ "bits_per_usd": {
+ "type": "number",
+ "description": "Today's Vibes per USD rate, the same one a pack is priced with."
+ }
+ }
+ },
"common.config-change.v1": {
"common.config-change.v1": {
"title": "ConfigChange",
"title": "ConfigChange",
"description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
"description": "common.config-change@1 (AI Exchange D11): body of POST /internal/config/:namespace on a service's openvibe-shared/config admin routes. It proposes a new revision of the namespace and activates it (the store validates against the namespace schema, applies env pins and calls onActivate). `merge: true` starts from the active values; otherwise `values` is the whole new set. `unset` removes keys. A secret-class key's value is never sent here: it is 422 config.secret_write_only; use common.config-secret-put@1.",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Codes API",
"title": "OpenVibe.Codes API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Domains API",
"title": "OpenVibe.Domains API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Food API",
"title": "OpenVibe.Food API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -26,8 +26,8 @@
@@ -26,8 +26,8 @@
"service": "billing",
"service": "billing",
"name": "OpenVibe.Billing",
"name": "OpenVibe.Billing",
"origin": "https://billing.openvibe.network",
"origin": "https://billing.openvibe.network",
- "operations": 43,
- "capabilities": 17
+ "operations": 47,
+ "capabilities": 19
},
},
{
{
"service": "blog",
"service": "blog",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.MediaHub API",
"title": "OpenVibe.MediaHub API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.288.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Play API",
"title": "OpenVibe.Play API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Quest API",
"title": "OpenVibe.Quest API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Report API",
"title": "OpenVibe.Report API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Today API",
"title": "OpenVibe.Today API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Vision API",
"title": "OpenVibe.Vision API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Work API",
"title": "OpenVibe.Work API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Zone API",
"title": "OpenVibe.Zone API",
- "version": "0.288.0",
- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.288.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.289.0",
+ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.289.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -164241,24 +164241,309 @@ export type BillingBalanceReadResult =
@@ -164241,24 +164241,309 @@ export type BillingBalanceReadResult =
transaction: BillingTransaction;
transaction: BillingTransaction;
reversed_by: string[];
reversed_by: string[];
};
};
-
/** [email protected] (owner: billing) */
/** [email protected] (owner: billing) */
/**
/**
- * billing.balance@1: a person's Vibes balances, GET /api/v1/balances/:subject on OpenVibe.Billing (also returned after a recycle). credit is spendable (bought or given back), payable is what others gave the person (the only balance that can be cashed out), pending_payouts is held for requested cashouts.
+ * billing.balance@1: a Balance account (D12), held by a person (owner user:<usr_id>) or a project (owner project:<prj_id>). GET /api/me/balance answers { personal, projects } of these. balance_nano_usd is the whole account; held_nano_usd is reserved by active holds; available_nano_usd is spendable (balance = held + available). Money is integer nano-USD as decimal strings (D1), never a float. sandbox marks a sandbox account. An id is permanent, so this contract still also describes the Vibes wallet body that billing.balance.read has answered: a person's Vibes (currency vibes-bits), not a Balance. Each branch is closed with additionalProperties: false; new code sends the Balance branch.
*/
*/
-export interface BillingBalance {
- subject: SubjectRef;
+export type BillingBalance = {
+ subject?: SubjectRef;
+ currency?: "vibes-bits";
+ credit?: number;
+ payable?: number;
+ pending_payouts?: number;
+ /**
+ * What payable is worth in US cents at today's rate.
+ */
+ payable_value_cents?: number;
+ bits_per_usd?: number;
+} & (
+ | {
+ owner:
+ | {
+ type: "user";
+ id: string;
+ }
+ | {
+ type: "project";
+ id: string;
+ };
+ /**
+ * Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float.
+ */
+ balance_nano_usd: string;
+ /**
+ * Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float.
+ */
+ held_nano_usd: string;
+ /**
+ * Integer nano-USD (1e-9 USD), stored as a decimal string (D1), never a float.
+ */
+ available_nano_usd: string;
+ /**
+ * True for a sandbox account (D10): sandbox credit only, never real money.
+ */
+ sandbox?: boolean;
+ }
+ | {
+ subject: SubjectRef;
+ currency: "vibes-bits";
+ credit: number;
+ payable: number;
+ pending_payouts: number;
+ /**
+ * What payable is worth in US cents at today's rate.
+ */
+ payable_value_cents: number;
+ bits_per_usd: number;
+ }
+);
+
+/** [email protected] (owner: billing) */
+/**
+ * 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.
+ */
+export interface BillingVibesCharge {
+ /**
+ * ULID charge id.
+ */
+ id: string;
+ /**
+ * The app that created the charge (its app_ id).
+ */
+ app: string;
+ /**
+ * The app's project; paid Vibes land in that project's Vibes wallet.
+ */
+ project_id: string;
+ /**
+ * Price in whole Vibes. A positive integer; Vibes have no fractions.
+ */
+ amount: number;
+ /**
+ * What the person is paying for.
+ */
+ description: string;
+ /**
+ * The app's own reference for this charge. It is the idempotency key: a replay with the same reference returns the same charge.
+ */
+ reference: string;
+ status: "pending" | "paid" | "refunded" | "expired" | "cancelled";
+ /**
+ * OpenVibe's pay sheet for this charge, e.g. https://openvibe.network/pay/vch_...
+ */
+ pay_url: string;
+ /**
+ * Where the browser returns after paying; null when the app gave no return URL.
+ */
+ return_url: string | null;
+ created_at: string;
+ /**
+ * When the Vibes moved, or null while the charge is unpaid.
+ */
+ paid_at: string | null;
+ /**
+ * When the Vibes went back to the person, or null.
+ */
+ refunded_at: string | null;
+ /**
+ * The pay sheet stops accepting payment after this instant.
+ */
+ expires_at: string;
+ /**
+ * True for a sandbox app: sandbox Vibes only, worth nothing.
+ */
+ sandbox: boolean;
+}
+
+/** [email protected] (owner: billing) */
+/**
+ * billing.vibes-charge-create-request@1: body of POST /api/v1/vibes/charges, sent from the app's server with its app token (capability billing.vibes.charge). amount is whole Vibes. reference is the app's idempotency key: a replay with the same reference and body returns the same charge. return_url is optional. The app token decides the project, the sandbox environment and the owner-set limits.
+ */
+export interface BillingVibesChargeCreateRequest {
+ /**
+ * Price in whole Vibes, a positive integer; never a fraction or a nano-USD amount.
+ */
+ amount: number;
+ /**
+ * What the person is paying for.
+ */
+ description: string;
+ /**
+ * The app's own reference; it is the idempotency key.
+ */
+ reference: string;
+ /**
+ * Where the browser returns after paying. Optional; the app may instead poll the charge or wait for the webhook.
+ */
+ return_url?: string;
+}
+
+/** [email protected] (owner: billing) */
+/**
+ * billing.vibes-charge-refund-request@1: the optional body of POST /api/v1/vibes/charges/:id/refund. A refund is always for the whole charge: the Vibes go back from the project's Vibes wallet to the person. reason is recorded on the refund. No amount may be given, because a partial refund is not a Vibes Pay refund.
+ */
+export interface BillingVibesChargeRefundRequest {
+ /**
+ * Why the app refunded, for its own and the person's record.
+ */
+ reason?: string | null;
+}
+
+/** [email protected] (owner: billing) */
+/**
+ * billing.vibes-wallet@1: a person's own Vibes, answered by GET /api/v1/vibes/me (capability billing.vibes.read) to an app the person approved. credit is Vibes to spend; payable is Vibes the person received as a creator and may cash out under the payout rules (never into Balance); bits_per_usd is today's rate. Vibes are whole integers; this is not a Balance and carries no nano-USD.
+ */
+export interface BillingVibesWallet {
+ /**
+ * The person the app reads for (the approved delegation). Only a user, never an app or a service.
+ */
+ subject: {
+ type: "user";
+ id: string;
+ };
currency: "vibes-bits";
currency: "vibes-bits";
+ /**
+ * Vibes to spend.
+ */
credit: number;
credit: number;
+ /**
+ * Vibes received as a creator, to cash out; never Balance.
+ */
payable: number;
payable: number;
- pending_payouts: number;
/**
/**
- * What payable is worth in US cents at today's rate.
+ * Today's Vibes per USD rate, the same one a pack is priced with.
*/
*/
- payable_value_cents: number;
bits_per_usd: number;
bits_per_usd: number;
}
}
+/** [email protected] (owner: billing) */
+/**
+ * billing.balance-transfer-request@1: body of POST /api/me/balance/transfers (D12). Moves amount_nano_usd from the signed-in person's Balance to a project they administer, into that project's Balance. Money is positive integer nano-USD as a decimal string (D1), never a float; the transfer is refused when the Balance or the person's project role is short. idempotency_key makes the transfer exactly once: a replay with the same key and body returns the first answer and moves nothing.
+ */
+export interface BillingBalanceTransferRequest {
+ /**
+ * The project whose Balance receives the money; the person must administer it.
+ */
+ to_project_id: string;
+ /**
+ * A positive integer nano-USD (1e-9 USD) as a decimal string (D1), never a float.
+ */
+ amount_nano_usd: string;
+ /**
+ * Unique per mutation. A replay with the same key and body returns the first answer and changes nothing.
+ */
+ idempotency_key: string;
+}
+
+/** [email protected] (owner: billing) */
+/**
+ * billing.vibes-purchase-from-balance-request@1: body of POST /api/me/vibes/from-balance (D12). Buys a whole number of Vibes with the signed-in person's Balance at the same Vibes price as a pack, charging exact nano-USD; Vibes -> Balance is never allowed. vibes is a whole integer (Vibes have no fractions); the money side is integer nano-USD (D1), never a float. idempotency_key makes the purchase exactly once.
+ */
+export interface BillingVibesPurchaseFromBalanceRequest {
+ /**
+ * How many whole Vibes to buy.
+ */
+ vibes: number;
+ /**
+ * Unique per mutation. A replay with the same key and body returns the first answer and changes nothing.
+ */
+ idempotency_key: string;
+}
+
+/** [email protected] (owner: billing) */
+/**
+ * billing.vibes.charge.paid v1 (OpenVibe.Billing, written when a Vibes Pay charge is paid): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.
+ */
+export interface BillingVibesChargePaidPayload {
+ /**
+ * The charge; GET /api/v1/vibes/charges/:id returns its current state.
+ */
+ id: string;
+ /**
+ * The app that created the charge.
+ */
+ app: string;
+ /**
+ * The app's project, whose Vibes wallet received the charge.
+ */
+ project_id: string;
+ /**
+ * The whole number of Vibes paid.
+ */
+ amount: number;
+ description: string;
+ /**
+ * The app's reference, as given when the charge was created.
+ */
+ reference: string;
+ status: "paid";
+ created_at: string;
+ /**
+ * When the Vibes moved from the person to the project.
+ */
+ paid_at: string;
+ /**
+ * Null: a paid charge has not been refunded.
+ */
+ refunded_at: null;
+ expires_at: string;
+ /**
+ * True for a sandbox app: sandbox Vibes only, worth nothing.
+ */
+ sandbox: boolean;
+ /**
+ * Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer.
+ */
+ payer_ref: string;
+}
+
+/** [email protected] (owner: billing) */
+/**
+ * billing.vibes.charge.refunded v1 (OpenVibe.Billing, written when a Vibes Pay charge is refunded): the app's charge and the opaque payer reference. This is also the Vibes Pay webhook body (HMAC-SHA256 with the app's webhook secret, retried with backoff). It deliberately carries no person id and no email, only payer_ref, stable per app, so an app can recognise a returning payer without receiving their identity. amount is whole Vibes. Envelope: subject { type: vibes-charge, id: vch_... }, actor service:billing.
+ */
+export interface BillingVibesChargeRefundedPayload {
+ /**
+ * The charge; GET /api/v1/vibes/charges/:id returns its current state.
+ */
+ id: string;
+ /**
+ * The app that created the charge.
+ */
+ app: string;
+ /**
+ * The app's project, whose Vibes wallet received the charge.
+ */
+ project_id: string;
+ /**
+ * The whole number of Vibes paid.
+ */
+ amount: number;
+ description: string;
+ /**
+ * The app's reference, as given when the charge was created.
+ */
+ reference: string;
+ status: "refunded";
+ created_at: string;
+ /**
+ * When the Vibes moved from the person to the project.
+ */
+ paid_at: string;
+ /**
+ * When the Vibes went back to the person.
+ */
+ refunded_at: string;
+ expires_at: string;
+ /**
+ * True for a sandbox app: sandbox Vibes only, worth nothing.
+ */
+ sandbox: boolean;
+ /**
+ * Opaque reference for the paying person, stable per app. Never the person's id, email or any other identity; an app uses it to recognise a returning payer.
+ */
+ payer_ref: string;
+}
+
/** [email protected] (owner: billing) */
/** [email protected] (owner: billing) */
/**
/**
* billing.cashout-decision@1: bodies of billing.cashout.manage on OpenVibe.Billing (staff). POST /api/v1/cashouts/:id/approve: { payout_reference, payout_provider? } after the payout was sent (payout_provider defaults to paypal; refused while the cashout is in escrow). POST /api/v1/cashouts/:id/deny: { reason? } returns the bits to the person's payable. GET /api/v1/cashouts takes no body. Every mutating call needs an Idempotency-Key header (a replay with the same key and body returns the first answer) and is refused with 503 billing.frozen while the economy is frozen. Refusals are problem+json (billing.invalid_input, billing.invalid_amount, billing.insufficient_funds, …).
* billing.cashout-decision@1: bodies of billing.cashout.manage on OpenVibe.Billing (staff). POST /api/v1/cashouts/:id/approve: { payout_reference, payout_provider? } after the payout was sent (payout_provider defaults to paypal; refused while the cashout is in escrow). POST /api/v1/cashouts/:id/deny: { reason? } returns the bits to the person's payable. GET /api/v1/cashouts takes no body. Every mutating call needs an Idempotency-Key header (a replay with the same key and body returns the first answer) and is refused with 503 billing.frozen while the economy is frozen. Refusals are problem+json (billing.invalid_input, billing.invalid_amount, billing.insufficient_funds, …).
@@ -164902,12 +165187,17 @@ export interface BillingCreditSettlement {
@@ -164902,12 +165187,17 @@ export interface BillingCreditSettlement {
/** [email protected] (owner: billing) */
/** [email protected] (owner: billing) */
/**
/**
- * billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a (subject, project_id) account, priced at the given immutable rate version.
+ * billing.credit-hold-create-request@1: body of POST /internal/credits/holds. Reserves inference credit up to max_nano_usd against a Balance account, priced at the given immutable rate version. With project_id the hold spends that project's Balance; without it the hold is personal and spends the subject's own Balance (D12). An app subject always names its project.
*/
*/
-export interface BillingCreditHoldCreateRequest {
+export type BillingCreditHoldCreateRequest = {
+ [k: string]: unknown | undefined;
+} & {
idempotency_key: string;
idempotency_key: string;
subject: SubjectRef;
subject: SubjectRef;
- project_id: string;
+ /**
+ * The project whose Balance the hold spends. Optional: without it the hold is personal, on the subject's own Balance. Required when the subject is an app.
+ */
+ project_id?: string;
credential_id?: string | null;
credential_id?: string | null;
/**
/**
* Integer nano-USD (1e-9 USD), stored as a string.
* Integer nano-USD (1e-9 USD), stored as a string.
@@ -164923,7 +165213,7 @@ export interface BillingCreditHoldCreateRequest {
@@ -164923,7 +165213,7 @@ export interface BillingCreditHoldCreateRequest {
* 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.
* 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.
*/
*/
sandbox?: boolean;
sandbox?: boolean;
-}
+};
/** [email protected] (owner: billing) */
/** [email protected] (owner: billing) */
/**
/**
new file mode 100644
new file mode 100644
@@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
+{
+ "id": "billing.vibes.charge",
+ "version": "1.0",
+ "owner": "billing",
+ "status": "active",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "Sell something for Vibes as a developer app (Vibes Pay, D12). POST /api/v1/vibes/charges creates a charge from the app's token (billing.vibes-charge-create-request@1; idempotent on the app's reference) and answers billing.vibes-charge@1 with a pay_url; GET /api/v1/vibes/charges/:id reads it; POST /api/v1/vibes/charges/:id/refund returns the Vibes from the project's Vibes wallet to the person (billing.vibes-charge-refund-request@1). The person pays on OpenVibe's pay sheet; Billing moves the Vibes atomically and once to the app's project Vibes wallet, then POSTs a signed webhook (HMAC-SHA256 with the app's webhook secret, retried with backoff) whose body is billing.vibes.charge.paid@1 or billing.vibes.charge.refunded@1 — no person id or email, only an opaque payer_ref stable per app. A sandbox app charges sandbox Vibes only (test grants, worth nothing). The owner's billing.vibes settings cap the amount, the daily per-person spend and whether Vibes Pay is on.",
+ "permissions": [
+ "billing:vibes:charge"
+ ],
+ "resourceConstraints": [
+ "project"
+ ],
+ "quotaClass": "billing-vibes-write",
+ "events": [
+ "billing.vibes.charge.paid",
+ "billing.vibes.charge.refunded"
+ ],
+ "implementedBy": [
+ "POST /api/v1/vibes/charges",
+ "GET /api/v1/vibes/charges/:id",
+ "POST /api/v1/vibes/charges/:id/refund"
+ ],
+ "inputSchema": "billing.vibes-charge-create-request@1",
+ "outputSchema": "billing.vibes-charge@1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+{
+ "id": "billing.vibes.read",
+ "version": "1.0",
+ "owner": "billing",
+ "status": "active",
+ "visibility": "public",
+ "description": "Read a person's own Vibes as an app the person approved (D12): GET /api/v1/vibes/me with a person-delegated app token answers billing.vibes-wallet@1 — the Vibes to spend, the creator payable that can be cashed out (never into Balance) and today's bits_per_usd. The app sees only the person who approved it, never another person's Vibes.",
+ "permissions": [
+ "billing:vibes:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "billing-vibes-read",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/vibes/me"
+ ],
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "billing.vibes-wallet@1"
+}
@@ -36,7 +36,9 @@
@@ -36,7 +36,9 @@
"billing.credit.hold.settle",
"billing.credit.hold.settle",
"billing.credit.hold.release",
"billing.credit.hold.release",
"billing.credit.sandbox.grant",
"billing.credit.sandbox.grant",
- "billing.config.manage"
+ "billing.config.manage",
+ "billing.vibes.charge",
+ "billing.vibes.read"
],
],
"eventsProduced": [
"eventsProduced": [
"billing.transaction.settled",
"billing.transaction.settled",
@@ -48,7 +50,9 @@
@@ -48,7 +50,9 @@
"billing.cashout.denied",
"billing.cashout.denied",
"billing.staff.action",
"billing.staff.action",
"billing.receipt.external",
"billing.receipt.external",
- "billing.credit.settled"
+ "billing.credit.settled",
+ "billing.vibes.charge.paid",
+ "billing.vibes.charge.refunded"
],
],
"eventsConsumed": [],
"eventsConsumed": [],
"namespacesOwned": [
"namespacesOwned": [
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.288.0",
+ "version": "0.289.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",