openvibers/openvibe.contracts Commits
Release the billing.usage.record capability in OpenVibe.Contracts so… (#5)
Changes
@@@ -4,6 +4,18 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.85.0 — 2026-10-02++++**Billing records usage readings** (plan T5 lane F): the capability `billing.usage.record` (active, internal,++`billing:usage`, resource constraint `none`, quota class `ledger-write`), released from OpenVibe.Billing's proposal and++listed by `manifests/services/billing.json`. `POST /api/v1/usage` takes one `platform.usage-sample@1` reading (its++`inputSchema`) and stores it without moving money; the reading's `idempotency_key` deduplicates retries (same reading:++200 with the stored row; a different one under the same key: 409 `billing.usage_key_reused`). Listing all readings++(`GET /api/v1/usage`) stays under `billing.ledger.admin`. New `billing.usage-record-result@1` (its `outputSchema`):++`{ record }` with the stored row's `id`, `idempotency_key`, `project`, `subject`, `service`, `at`, `received_at`,++`principal` and the `reading` itself; fixtures `recorded` (valid) and `reading-without-idempotency-key` (invalid).++Network grants for the usage producers follow this release. Additive.++## 0.84.0 — 2026-10-02Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabilities and@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/billing/admin-result.v1.json","title": "BillingAdminResult",-- "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.",++ "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.","anyOf": [{"type": "object",@@@ -210,6 +210,69 @@}}}++ },++ {++ "type": "object",++ "required": [++ "records",++ "next_cursor"++ ],++ "properties": {++ "records": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "id",++ "idempotency_key",++ "service",++ "at",++ "received_at",++ "principal",++ "reading"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "The reading's own idempotency_key"++ },++ "project": {++ "type": ["string", "null"]++ },++ "subject": {++ "type": ["string", "null"]++ },++ "service": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "received_at": {++ "type": "string",++ "format": "date-time"++ },++ "principal": {++ "type": "string",++ "description": "The service principal that recorded the reading"++ },++ "reading": {++ "$ref": "../platform/usage-sample.v1.json"++ }++ }++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ }++ }}]}nnew file mode 100644@@@ -0,0 +1,63 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/usage-record-result.v1.json",++ "title": "BillingUsageRecordResult",++ "description": "billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.",++ "type": "object",++ "required": [++ "record"++ ],++ "properties": {++ "record": {++ "type": "object",++ "required": [++ "id",++ "idempotency_key",++ "service",++ "at",++ "received_at",++ "principal",++ "reading"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "The reading's own idempotency_key"++ },++ "project": {++ "type": [++ "string",++ "null"++ ]++ },++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "service": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "received_at": {++ "type": "string",++ "format": "date-time"++ },++ "principal": {++ "type": "string",++ "description": "The service principal that recorded the reading"++ },++ "reading": {++ "$ref": "../platform/usage-sample.v1.json"++ }++ }++ }++ }++}@@@ -2229,6 +2229,15 @@"compatibility": "backward","status": "active"},++ {++ "id": "billing.usage-record-result",++ "version": "1.0.0",++ "owner": "billing",++ "schema": "billing/usage-record-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "deals.watch.matched","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,29 @@++{++ "records": [++ {++ "id": "1",++ "idempotency_key": "media:delivery:2",++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "service": "media",++ "at": "2026-09-30T10:00:00.000Z",++ "received_at": "2026-09-30T10:00:01.000Z",++ "principal": "media",++ "reading": {++ "id": "use-2",++ "idempotency_key": "media:delivery:2",++ "service": "media",++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "operation": "deliver",++ "quantity": 1.5,++ "unit": "GiB",++ "at": "2026-09-30T10:00:00Z",++ "source": "media.egress",++ "free_allowance_used": 1,++ "vibes_charged": 4++ }++ }++ ],++ "next_cursor": null++}nnew file mode 100644@@@ -0,0 +1 @@++{ "record": { "id": "1", "idempotency_key": "media:delivery:2", "project": null, "subject": null, "service": "media", "at": "2026-09-30T10:00:00.000Z", "received_at": "2026-09-30T10:00:01.000Z", "principal": "media", "reading": { "id": "use-2", "service": "media", "operation": "deliver", "quantity": 1.5, "unit": "GiB", "at": "2026-09-30T10:00:00Z", "source": "media.egress" } } }nnew file mode 100644@@@ -0,0 +1 @@++{ "record": { "id": "1", "idempotency_key": "media:delivery:2", "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ", "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "service": "media", "at": "2026-09-30T10:00:00.000Z", "received_at": "2026-09-30T10:00:01.000Z", "principal": "media", "reading": { "id": "use-2", "idempotency_key": "media:delivery:2", "service": "media", "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ", "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "operation": "deliver", "quantity": 1.5, "unit": "GiB", "at": "2026-09-30T10:00:00Z", "source": "media.egress", "free_allowance_used": 1, "vibes_charged": 4 } } }@@@ -22102,7 +22102,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/billing/admin-result.v1.json","title": "BillingAdminResult",-- "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.",++ "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.","anyOf": [{"type": "object",@@@ -22310,6 +22310,75 @@}}}++ },++ {++ "type": "object",++ "required": [++ "records",++ "next_cursor"++ ],++ "properties": {++ "records": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "id",++ "idempotency_key",++ "service",++ "at",++ "received_at",++ "principal",++ "reading"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "The reading's own idempotency_key"++ },++ "project": {++ "type": [++ "string",++ "null"++ ]++ },++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "service": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "received_at": {++ "type": "string",++ "format": "date-time"++ },++ "principal": {++ "type": "string",++ "description": "The service principal that recorded the reading"++ },++ "reading": {++ "$ref": "../platform/usage-sample.v1.json"++ }++ }++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ }++ }}]}@@@ -23575,6 +23644,74 @@}}},++ "billing.usage-record-result": {++ "version": "1.0.0",++ "owner": "billing",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/billing/usage-record-result.v1.json",++ "title": "BillingUsageRecordResult",++ "description": "billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.",++ "type": "object",++ "required": [++ "record"++ ],++ "properties": {++ "record": {++ "type": "object",++ "required": [++ "id",++ "idempotency_key",++ "service",++ "at",++ "received_at",++ "principal",++ "reading"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "The reading's own idempotency_key"++ },++ "project": {++ "type": [++ "string",++ "null"++ ]++ },++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "service": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "received_at": {++ "type": "string",++ "format": "date-time"++ },++ "principal": {++ "type": "string",++ "description": "The service principal that recorded the reading"++ },++ "reading": {++ "$ref": "../platform/usage-sample.v1.json"++ }++ }++ }++ }++ }++ },"deals.watch.matched": {"version": "1.0.0","owner": "deals",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.84.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.85.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"@@@ -1622,6 +1622,109 @@}}}++ },++ "/api/v1/usage": {++ "get": {++ "operationId": "get_api_v1_usage",++ "summary": "Operate the ledger: economy freeze switch (writes refused, reads served, webhooks queued), reconciliation runs, balanced manual adjustments with a reason, settling a verified provider receipt, provide",++ "description": "**billing.ledger.admin** (internal): Operate the ledger: economy freeze switch (writes refused, reads served, webhooks queued), reconciliation runs, balanced manual adjustments with a reason, settling a verified provider receipt, provider-event reprocessing, the renewal sweep and import holds.",++ "tags": [++ "billing.ledger"++ ],++ "x-openvibe-capabilities": [++ "billing.ledger.admin"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "ledger-admin",++ "security": [++ {++ "openvibeToken": [++ "billing.ledger.admin"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/billing.admin-request.v1"++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.admin-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_usage",++ "summary": "Record a platform.usage-sample@1 reading without moving money.",++ "description": "**billing.usage.record** (internal): Record a platform.usage-sample@1 reading without moving money. The reading's idempotency_key deduplicates retries. Listing all readings requires billing.ledger.admin.",++ "tags": [++ "billing.usage"++ ],++ "x-openvibe-capabilities": [++ "billing.usage.record"++ ],++ "x-openvibe-visibility": [++ "internal"++ ],++ "x-openvibe-quota-class": "ledger-write",++ "security": [++ {++ "openvibeToken": [++ "billing.usage.record"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/platform.usage-sample.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/billing.usage-record-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }}},"components": {@@@ -1806,7 +1909,7 @@},"billing.admin-result.v1": {"title": "BillingAdminResult",-- "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.",++ "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.","anyOf": [{"type": "object",@@@ -2014,6 +2117,75 @@}}}++ },++ {++ "type": "object",++ "required": [++ "records",++ "next_cursor"++ ],++ "properties": {++ "records": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "id",++ "idempotency_key",++ "service",++ "at",++ "received_at",++ "principal",++ "reading"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "The reading's own idempotency_key"++ },++ "project": {++ "type": [++ "string",++ "null"++ ]++ },++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "service": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "received_at": {++ "type": "string",++ "format": "date-time"++ },++ "principal": {++ "type": "string",++ "description": "The service principal that recorded the reading"++ },++ "reading": {++ "$ref": "#/components/schemas/platform.usage-sample.v1"++ }++ }++ }++ },++ "next_cursor": {++ "type": [++ "string",++ "null"++ ]++ }++ }}]},@@@ -3159,6 +3331,67 @@}}},++ "billing.usage-record-result.v1": {++ "title": "BillingUsageRecordResult",++ "description": "billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.",++ "type": "object",++ "required": [++ "record"++ ],++ "properties": {++ "record": {++ "type": "object",++ "required": [++ "id",++ "idempotency_key",++ "service",++ "at",++ "received_at",++ "principal",++ "reading"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "The reading's own idempotency_key"++ },++ "project": {++ "type": [++ "string",++ "null"++ ]++ },++ "subject": {++ "type": [++ "string",++ "null"++ ]++ },++ "service": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "received_at": {++ "type": "string",++ "format": "date-time"++ },++ "principal": {++ "type": "string",++ "description": "The service principal that recorded the reading"++ },++ "reading": {++ "$ref": "#/components/schemas/platform.usage-sample.v1"++ }++ }++ }++ }++ },"common.entity-ref.v1": {"title": "EntityRef","description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",@@@ -3382,6 +3615,138 @@"description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."}]++ },++ "platform.usage-sample.v1": {++ "title": "UsageSample",++ "description": "One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed). Consumers dedupe on `idempotency_key`.",++ "$comment": "Additive optional fields may extend this v1 contract.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "idempotency_key",++ "service",++ "operation",++ "quantity",++ "unit",++ "at",++ "source"++ ],++ "properties": {++ "id": {++ "type": "string"++ },++ "idempotency_key": {++ "type": "string",++ "description": "Stable across retries"++ },++ "service": {++ "type": "string"++ },++ "project": {++ "type": "string",++ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "subject": {++ "type": "string"++ },++ "resource": {++ "type": "string"++ },++ "provider": {++ "type": "string"++ },++ "node": {++ "type": "string"++ },++ "cell": {++ "type": "string"++ },++ "region": {++ "type": "string"++ },++ "operation": {++ "type": "string"++ },++ "quantity": {++ "type": "number",++ "minimum": 0++ },++ "unit": {++ "type": "string"++ },++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "cost_estimate": {++ "type": "number",++ "minimum": 0,++ "description": "Estimated USD"++ },++ "free_allowance_used": {++ "type": "number",++ "minimum": 0,++ "description": "How much of this reading's `quantity` the free allowance covered (platform.rate-card@1 `free_allowance` for this provider/metric and period), in the reading's own `unit`, not in rate-card units, money or Vibes: a 1.5 GiB reading with 1 GiB free carries 1. Never more than `quantity`; 0 when nothing was free. Absent: not rated against a rate card."++ },++ "vibes_charged": {++ "type": "integer",++ "minimum": 0,++ "description": "Vibes charged for this reading, as an integer count of vibes-bits (Billing's ledger minor unit, currency `vibes-bits`; balance credit and ledger amounts use the same unit). Never whole Vibes, USD or a fraction: round once, when rating. Covers only `quantity` minus `free_allowance_used`; 0 means rated and nothing charged. Absent means not rated (yet). Records a charge already made in Billing's ledger; it is never a request to charge."++ },++ "route_epoch": {++ "type": "integer",++ "minimum": 0++ },++ "trace_id": {++ "type": "string"++ },++ "source": {++ "type": "string"++ }++ },++ "examples": [++ {++ "id": "use-1",++ "idempotency_key": "media:delivery:1",++ "service": "media",++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "resource": "object-1",++ "provider": "local",++ "node": "node-1",++ "cell": "cell-west",++ "region": "us-west",++ "operation": "deliver",++ "quantity": 1.5,++ "unit": "GiB",++ "at": "2026-09-30T10:00:00Z",++ "cost_estimate": 0.03,++ "source": "media.egress"++ },++ {++ "id": "use-2",++ "idempotency_key": "media:delivery:2",++ "service": "media",++ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "resource": "object-1",++ "provider": "local",++ "node": "node-1",++ "cell": "cell-west",++ "region": "us-west",++ "operation": "deliver",++ "quantity": 1.5,++ "unit": "GiB",++ "at": "2026-09-30T10:00:00Z",++ "cost_estimate": 0.03,++ "source": "media.egress",++ "free_allowance_used": 1,++ "vibes_charged": 4,++ "route_epoch": 3,++ "trace_id": "tr-42"++ }++ ]}},"securitySchemes": {@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.84.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.85.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"@@@ -12,8 +12,8 @@"service": "billing","name": "OpenVibe.Billing","origin": "https://billing.openvibe.network",-- "operations": 28,-- "capabilities": 8++ "operations": 30,++ "capabilities": 9},{"service": "blog",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.84.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.85.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": "OpenRe.Stream API",-- "version": "0.84.0",-- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.85.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"@@@ -28805,7 +28805,7 @@ export type BillingAdminRequest =/** [email protected] (owner: billing) *//**-- * billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.++ * billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.*/export type BillingAdminResult =| {@@@ -28855,6 +28855,26 @@ export type BillingAdminResult =}| {holds: {}[];++ }++ | {++ records: {++ id: string;++ /**++ * The reading's own idempotency_key++ */++ idempotency_key: string;++ project?: string | null;++ subject?: string | null;++ service: string;++ at: string;++ received_at: string;++ /**++ * The service principal that recorded the reading++ */++ principal: string;++ reading: UsageSample;++ }[];++ next_cursor: string | null;};/** [email protected] (owner: billing) */@@@ -29302,6 +29322,30 @@ export interface BillingTransferResult {};}++/** [email protected] (owner: billing) */++/**++ * billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.++ */++export interface BillingUsageRecordResult {++ record: {++ id: string;++ /**++ * The reading's own idempotency_key++ */++ idempotency_key: string;++ project?: string | null;++ subject?: string | null;++ service: string;++ at: string;++ received_at: string;++ /**++ * The service principal that recorded the reading++ */++ principal: string;++ reading: UsageSample;++ };++}++/** [email protected] (owner: deals) *//*** deals.watch.matched v1 (OpenVibe.Deals server/domain/watches.js onObservation). A person's keyword, product or price-below watch matched a fresh observation of an active offer, inside the transaction that recorded the observation. At most one event per (watch, observation); a keyword/product watch notifies once per offer, a price-below watch again only for a lower price. Saved searches (kind search) never notify. Envelope: subject { type: watch, id: <watch_id> }, visibility internal, priority important, actor service:deals. OpenVibe.Network turns it into a DEAL_WATCH_MATCH notification for payload.recipient (server/notifications/events-consumer.js). A price is the decimal exactly as stated, never inferred; null when the observation stated none.@@@ -23,7 +23,8 @@"GET /api/v1/admin/provider-events","POST /api/v1/admin/provider-events/:id/reprocess","POST /api/v1/admin/sweep",-- "GET /api/v1/admin/import-holds"++ "GET /api/v1/admin/import-holds",++ "GET /api/v1/usage"],"inputSchema": "billing.admin-request@1","outputSchema": "billing.admin-result@1"nnew file mode 100644@@@ -0,0 +1,21 @@++{++ "id": "billing.usage.record",++ "version": "1.0",++ "owner": "billing",++ "status": "active",++ "visibility": "internal",++ "description": "Record a platform.usage-sample@1 reading without moving money. The reading's idempotency_key deduplicates retries. Listing all readings requires billing.ledger.admin.",++ "permissions": [++ "billing:usage"++ ],++ "resourceConstraints": [++ "none"++ ],++ "quotaClass": "ledger-write",++ "events": [],++ "implementedBy": [++ "POST /api/v1/usage"++ ],++ "inputSchema": "platform.usage-sample@1",++ "outputSchema": "billing.usage-record-result@1"++}@@@ -27,7 +27,8 @@"billing.cashout.manage","billing.subscription.manage","billing.entitlement.check",-- "billing.ledger.admin"++ "billing.ledger.admin",++ "billing.usage.record"],"eventsProduced": ["billing.transaction.settled",@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.84.0",++ "version": "0.85.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.84.0",++ "version": "0.85.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.84.0",++ "version": "0.85.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",
@@ -4,6 +4,18 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,18 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.85.0 — 2026-10-02
+
+**Billing records usage readings** (plan T5 lane F): the capability `billing.usage.record` (active, internal,
+`billing:usage`, resource constraint `none`, quota class `ledger-write`), released from OpenVibe.Billing's proposal and
+listed by `manifests/services/billing.json`. `POST /api/v1/usage` takes one `platform.usage-sample@1` reading (its
+`inputSchema`) and stores it without moving money; the reading's `idempotency_key` deduplicates retries (same reading:
+200 with the stored row; a different one under the same key: 409 `billing.usage_key_reused`). Listing all readings
+(`GET /api/v1/usage`) stays under `billing.ledger.admin`. New `billing.usage-record-result@1` (its `outputSchema`):
+`{ record }` with the stored row's `id`, `idempotency_key`, `project`, `subject`, `service`, `at`, `received_at`,
+`principal` and the `reading` itself; fixtures `recorded` (valid) and `reading-without-idempotency-key` (invalid).
+Network grants for the usage producers follow this release. Additive.
+
## 0.84.0 — 2026-10-02
## 0.84.0 — 2026-10-02
Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabilities and
Add nine public `platform.*@1` schemas: usage and telemetry samples; node capabilities and
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/billing/admin-result.v1.json",
"$id": "https://openvibe.network/contracts/billing/admin-result.v1.json",
"title": "BillingAdminResult",
"title": "BillingAdminResult",
- "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.",
+ "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.",
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
@@ -210,6 +210,69 @@
@@ -210,6 +210,69 @@
}
}
}
}
}
}
+ },
+ {
+ "type": "object",
+ "required": [
+ "records",
+ "next_cursor"
+ ],
+ "properties": {
+ "records": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "at",
+ "received_at",
+ "principal",
+ "reading"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "The reading's own idempotency_key"
+ },
+ "project": {
+ "type": ["string", "null"]
+ },
+ "subject": {
+ "type": ["string", "null"]
+ },
+ "service": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "received_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "principal": {
+ "type": "string",
+ "description": "The service principal that recorded the reading"
+ },
+ "reading": {
+ "$ref": "../platform/usage-sample.v1.json"
+ }
+ }
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/usage-record-result.v1.json",
+ "title": "BillingUsageRecordResult",
+ "description": "billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.",
+ "type": "object",
+ "required": [
+ "record"
+ ],
+ "properties": {
+ "record": {
+ "type": "object",
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "at",
+ "received_at",
+ "principal",
+ "reading"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "The reading's own idempotency_key"
+ },
+ "project": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "service": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "received_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "principal": {
+ "type": "string",
+ "description": "The service principal that recorded the reading"
+ },
+ "reading": {
+ "$ref": "../platform/usage-sample.v1.json"
+ }
+ }
+ }
+ }
+}
@@ -2229,6 +2229,15 @@
@@ -2229,6 +2229,15 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "billing.usage-record-result",
+ "version": "1.0.0",
+ "owner": "billing",
+ "schema": "billing/usage-record-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "deals.watch.matched",
"id": "deals.watch.matched",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
+{
+ "records": [
+ {
+ "id": "1",
+ "idempotency_key": "media:delivery:2",
+ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "service": "media",
+ "at": "2026-09-30T10:00:00.000Z",
+ "received_at": "2026-09-30T10:00:01.000Z",
+ "principal": "media",
+ "reading": {
+ "id": "use-2",
+ "idempotency_key": "media:delivery:2",
+ "service": "media",
+ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "operation": "deliver",
+ "quantity": 1.5,
+ "unit": "GiB",
+ "at": "2026-09-30T10:00:00Z",
+ "source": "media.egress",
+ "free_allowance_used": 1,
+ "vibes_charged": 4
+ }
+ }
+ ],
+ "next_cursor": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "record": { "id": "1", "idempotency_key": "media:delivery:2", "project": null, "subject": null, "service": "media", "at": "2026-09-30T10:00:00.000Z", "received_at": "2026-09-30T10:00:01.000Z", "principal": "media", "reading": { "id": "use-2", "service": "media", "operation": "deliver", "quantity": 1.5, "unit": "GiB", "at": "2026-09-30T10:00:00Z", "source": "media.egress" } } }
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{ "record": { "id": "1", "idempotency_key": "media:delivery:2", "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ", "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "service": "media", "at": "2026-09-30T10:00:00.000Z", "received_at": "2026-09-30T10:00:01.000Z", "principal": "media", "reading": { "id": "use-2", "idempotency_key": "media:delivery:2", "service": "media", "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ", "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ", "operation": "deliver", "quantity": 1.5, "unit": "GiB", "at": "2026-09-30T10:00:00Z", "source": "media.egress", "free_allowance_used": 1, "vibes_charged": 4 } } }
@@ -22102,7 +22102,7 @@
@@ -22102,7 +22102,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/billing/admin-result.v1.json",
"$id": "https://openvibe.network/contracts/billing/admin-result.v1.json",
"title": "BillingAdminResult",
"title": "BillingAdminResult",
- "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.",
+ "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.",
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
@@ -22310,6 +22310,75 @@
@@ -22310,6 +22310,75 @@
}
}
}
}
}
}
+ },
+ {
+ "type": "object",
+ "required": [
+ "records",
+ "next_cursor"
+ ],
+ "properties": {
+ "records": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "at",
+ "received_at",
+ "principal",
+ "reading"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "The reading's own idempotency_key"
+ },
+ "project": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "service": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "received_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "principal": {
+ "type": "string",
+ "description": "The service principal that recorded the reading"
+ },
+ "reading": {
+ "$ref": "../platform/usage-sample.v1.json"
+ }
+ }
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
}
}
]
]
}
}
@@ -23575,6 +23644,74 @@
@@ -23575,6 +23644,74 @@
}
}
}
}
},
},
+ "billing.usage-record-result": {
+ "version": "1.0.0",
+ "owner": "billing",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/billing/usage-record-result.v1.json",
+ "title": "BillingUsageRecordResult",
+ "description": "billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.",
+ "type": "object",
+ "required": [
+ "record"
+ ],
+ "properties": {
+ "record": {
+ "type": "object",
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "at",
+ "received_at",
+ "principal",
+ "reading"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "The reading's own idempotency_key"
+ },
+ "project": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "service": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "received_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "principal": {
+ "type": "string",
+ "description": "The service principal that recorded the reading"
+ },
+ "reading": {
+ "$ref": "../platform/usage-sample.v1.json"
+ }
+ }
+ }
+ }
+ }
+ },
"deals.watch.matched": {
"deals.watch.matched": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "deals",
"owner": "deals",
@@ -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.84.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.85.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"
@@ -1622,6 +1622,109 @@
@@ -1622,6 +1622,109 @@
}
}
}
}
}
}
+ },
+ "/api/v1/usage": {
+ "get": {
+ "operationId": "get_api_v1_usage",
+ "summary": "Operate the ledger: economy freeze switch (writes refused, reads served, webhooks queued), reconciliation runs, balanced manual adjustments with a reason, settling a verified provider receipt, provide",
+ "description": "**billing.ledger.admin** (internal): Operate the ledger: economy freeze switch (writes refused, reads served, webhooks queued), reconciliation runs, balanced manual adjustments with a reason, settling a verified provider receipt, provider-event reprocessing, the renewal sweep and import holds.",
+ "tags": [
+ "billing.ledger"
+ ],
+ "x-openvibe-capabilities": [
+ "billing.ledger.admin"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "ledger-admin",
+ "security": [
+ {
+ "openvibeToken": [
+ "billing.ledger.admin"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/billing.admin-request.v1"
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.admin-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_usage",
+ "summary": "Record a platform.usage-sample@1 reading without moving money.",
+ "description": "**billing.usage.record** (internal): Record a platform.usage-sample@1 reading without moving money. The reading's idempotency_key deduplicates retries. Listing all readings requires billing.ledger.admin.",
+ "tags": [
+ "billing.usage"
+ ],
+ "x-openvibe-capabilities": [
+ "billing.usage.record"
+ ],
+ "x-openvibe-visibility": [
+ "internal"
+ ],
+ "x-openvibe-quota-class": "ledger-write",
+ "security": [
+ {
+ "openvibeToken": [
+ "billing.usage.record"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/platform.usage-sample.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/billing.usage-record-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"components": {
"components": {
@@ -1806,7 +1909,7 @@
@@ -1806,7 +1909,7 @@
},
},
"billing.admin-result.v1": {
"billing.admin-result.v1": {
"title": "BillingAdminResult",
"title": "BillingAdminResult",
- "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.",
+ "description": "billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.",
"anyOf": [
"anyOf": [
{
{
"type": "object",
"type": "object",
@@ -2014,6 +2117,75 @@
@@ -2014,6 +2117,75 @@
}
}
}
}
}
}
+ },
+ {
+ "type": "object",
+ "required": [
+ "records",
+ "next_cursor"
+ ],
+ "properties": {
+ "records": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "at",
+ "received_at",
+ "principal",
+ "reading"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "The reading's own idempotency_key"
+ },
+ "project": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "service": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "received_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "principal": {
+ "type": "string",
+ "description": "The service principal that recorded the reading"
+ },
+ "reading": {
+ "$ref": "#/components/schemas/platform.usage-sample.v1"
+ }
+ }
+ }
+ },
+ "next_cursor": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
}
}
]
]
},
},
@@ -3159,6 +3331,67 @@
@@ -3159,6 +3331,67 @@
}
}
}
}
},
},
+ "billing.usage-record-result.v1": {
+ "title": "BillingUsageRecordResult",
+ "description": "billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.",
+ "type": "object",
+ "required": [
+ "record"
+ ],
+ "properties": {
+ "record": {
+ "type": "object",
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "at",
+ "received_at",
+ "principal",
+ "reading"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "The reading's own idempotency_key"
+ },
+ "project": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "subject": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "service": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "received_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "principal": {
+ "type": "string",
+ "description": "The service principal that recorded the reading"
+ },
+ "reading": {
+ "$ref": "#/components/schemas/platform.usage-sample.v1"
+ }
+ }
+ }
+ }
+ },
"common.entity-ref.v1": {
"common.entity-ref.v1": {
"title": "EntityRef",
"title": "EntityRef",
"description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",
"description": "Typed reference to another service's entity. Store this instead of a foreign key; resolve a display projection from the owner.",
@@ -3382,6 +3615,138 @@
@@ -3382,6 +3615,138 @@
"description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
"description": "AgentSubject: an agent (OpenVibe.Actor, or a developer app's agent) acting under grants its owner delegated (roadmap WS-Z2). In an event envelope, on_behalf_of names the owner."
}
}
]
]
+ },
+ "platform.usage-sample.v1": {
+ "title": "UsageSample",
+ "description": "One metered usage reading with retry-safe attribution (T1 Universal Fabric; ADR-034 proposed). Consumers dedupe on `idempotency_key`.",
+ "$comment": "Additive optional fields may extend this v1 contract.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "idempotency_key",
+ "service",
+ "operation",
+ "quantity",
+ "unit",
+ "at",
+ "source"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "idempotency_key": {
+ "type": "string",
+ "description": "Stable across retries"
+ },
+ "service": {
+ "type": "string"
+ },
+ "project": {
+ "type": "string",
+ "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "subject": {
+ "type": "string"
+ },
+ "resource": {
+ "type": "string"
+ },
+ "provider": {
+ "type": "string"
+ },
+ "node": {
+ "type": "string"
+ },
+ "cell": {
+ "type": "string"
+ },
+ "region": {
+ "type": "string"
+ },
+ "operation": {
+ "type": "string"
+ },
+ "quantity": {
+ "type": "number",
+ "minimum": 0
+ },
+ "unit": {
+ "type": "string"
+ },
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "cost_estimate": {
+ "type": "number",
+ "minimum": 0,
+ "description": "Estimated USD"
+ },
+ "free_allowance_used": {
+ "type": "number",
+ "minimum": 0,
+ "description": "How much of this reading's `quantity` the free allowance covered (platform.rate-card@1 `free_allowance` for this provider/metric and period), in the reading's own `unit`, not in rate-card units, money or Vibes: a 1.5 GiB reading with 1 GiB free carries 1. Never more than `quantity`; 0 when nothing was free. Absent: not rated against a rate card."
+ },
+ "vibes_charged": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Vibes charged for this reading, as an integer count of vibes-bits (Billing's ledger minor unit, currency `vibes-bits`; balance credit and ledger amounts use the same unit). Never whole Vibes, USD or a fraction: round once, when rating. Covers only `quantity` minus `free_allowance_used`; 0 means rated and nothing charged. Absent means not rated (yet). Records a charge already made in Billing's ledger; it is never a request to charge."
+ },
+ "route_epoch": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "trace_id": {
+ "type": "string"
+ },
+ "source": {
+ "type": "string"
+ }
+ },
+ "examples": [
+ {
+ "id": "use-1",
+ "idempotency_key": "media:delivery:1",
+ "service": "media",
+ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "resource": "object-1",
+ "provider": "local",
+ "node": "node-1",
+ "cell": "cell-west",
+ "region": "us-west",
+ "operation": "deliver",
+ "quantity": 1.5,
+ "unit": "GiB",
+ "at": "2026-09-30T10:00:00Z",
+ "cost_estimate": 0.03,
+ "source": "media.egress"
+ },
+ {
+ "id": "use-2",
+ "idempotency_key": "media:delivery:2",
+ "service": "media",
+ "project": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "subject": "user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "resource": "object-1",
+ "provider": "local",
+ "node": "node-1",
+ "cell": "cell-west",
+ "region": "us-west",
+ "operation": "deliver",
+ "quantity": 1.5,
+ "unit": "GiB",
+ "at": "2026-09-30T10:00:00Z",
+ "cost_estimate": 0.03,
+ "source": "media.egress",
+ "free_allowance_used": 1,
+ "vibes_charged": 4,
+ "route_epoch": 3,
+ "trace_id": "tr-42"
+ }
+ ]
}
}
},
},
"securitySchemes": {
"securitySchemes": {
@@ -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.84.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.85.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"
@@ -12,8 +12,8 @@
@@ -12,8 +12,8 @@
"service": "billing",
"service": "billing",
"name": "OpenVibe.Billing",
"name": "OpenVibe.Billing",
"origin": "https://billing.openvibe.network",
"origin": "https://billing.openvibe.network",
- "operations": 28,
- "capabilities": 8
+ "operations": 30,
+ "capabilities": 9
},
},
{
{
"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.Live API",
"title": "OpenVibe.Live API",
- "version": "0.84.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.85.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": "OpenRe.Stream API",
"title": "OpenRe.Stream API",
- "version": "0.84.0",
- "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenRe.Stream as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.85.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.84.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.84.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.85.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.85.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"
@@ -28805,7 +28805,7 @@ export type BillingAdminRequest =
@@ -28805,7 +28805,7 @@ export type BillingAdminRequest =
/** [email protected] (owner: billing) */
/** [email protected] (owner: billing) */
/**
/**
- * billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }.
+ * billing.admin-result@1: answers of billing.ledger.admin on OpenVibe.Billing. GET|POST /api/v1/admin/freeze → { frozen, reason, frozen_at, frozen_by } (POST adds processed_after_unfreeze); GET /api/v1/admin/reconcile → the reconciliation report { id, ok, trigger, started_at, finished_at, checks, warnings, totals }; POST /api/v1/admin/adjustments and POST /api/v1/purchases/settle → { transaction } (settle adds duplicate_receipt); GET /api/v1/admin/provider-events → { events }; POST …/provider-events/:id/reprocess → { event }; POST /api/v1/admin/sweep → { renewed, expired, canceled, skipped }; GET /api/v1/admin/import-holds → { holds }; GET /api/v1/usage → { records, next_cursor }.
*/
*/
export type BillingAdminResult =
export type BillingAdminResult =
| {
| {
@@ -28855,6 +28855,26 @@ export type BillingAdminResult =
@@ -28855,6 +28855,26 @@ export type BillingAdminResult =
}
}
| {
| {
holds: {}[];
holds: {}[];
+ }
+ | {
+ records: {
+ id: string;
+ /**
+ * The reading's own idempotency_key
+ */
+ idempotency_key: string;
+ project?: string | null;
+ subject?: string | null;
+ service: string;
+ at: string;
+ received_at: string;
+ /**
+ * The service principal that recorded the reading
+ */
+ principal: string;
+ reading: UsageSample;
+ }[];
+ next_cursor: string | null;
};
};
/** [email protected] (owner: billing) */
/** [email protected] (owner: billing) */
@@ -29302,6 +29322,30 @@ export interface BillingTransferResult {
@@ -29302,6 +29322,30 @@ export interface BillingTransferResult {
};
};
}
}
+/** [email protected] (owner: billing) */
+/**
+ * billing.usage-record-result@1: answer of billing.usage.record on OpenVibe.Billing. POST /api/v1/usage → { record } (201 when stored; 200 with `Idempotent-Replayed: true` when the same reading was already stored under its idempotency_key). Storing a reading charges nothing.
+ */
+export interface BillingUsageRecordResult {
+ record: {
+ id: string;
+ /**
+ * The reading's own idempotency_key
+ */
+ idempotency_key: string;
+ project?: string | null;
+ subject?: string | null;
+ service: string;
+ at: string;
+ received_at: string;
+ /**
+ * The service principal that recorded the reading
+ */
+ principal: string;
+ reading: UsageSample;
+ };
+}
+
/** [email protected] (owner: deals) */
/** [email protected] (owner: deals) */
/**
/**
* deals.watch.matched v1 (OpenVibe.Deals server/domain/watches.js onObservation). A person's keyword, product or price-below watch matched a fresh observation of an active offer, inside the transaction that recorded the observation. At most one event per (watch, observation); a keyword/product watch notifies once per offer, a price-below watch again only for a lower price. Saved searches (kind search) never notify. Envelope: subject { type: watch, id: <watch_id> }, visibility internal, priority important, actor service:deals. OpenVibe.Network turns it into a DEAL_WATCH_MATCH notification for payload.recipient (server/notifications/events-consumer.js). A price is the decimal exactly as stated, never inferred; null when the observation stated none.
* deals.watch.matched v1 (OpenVibe.Deals server/domain/watches.js onObservation). A person's keyword, product or price-below watch matched a fresh observation of an active offer, inside the transaction that recorded the observation. At most one event per (watch, observation); a keyword/product watch notifies once per offer, a price-below watch again only for a lower price. Saved searches (kind search) never notify. Envelope: subject { type: watch, id: <watch_id> }, visibility internal, priority important, actor service:deals. OpenVibe.Network turns it into a DEAL_WATCH_MATCH notification for payload.recipient (server/notifications/events-consumer.js). A price is the decimal exactly as stated, never inferred; null when the observation stated none.
@@ -23,7 +23,8 @@
@@ -23,7 +23,8 @@
"GET /api/v1/admin/provider-events",
"GET /api/v1/admin/provider-events",
"POST /api/v1/admin/provider-events/:id/reprocess",
"POST /api/v1/admin/provider-events/:id/reprocess",
"POST /api/v1/admin/sweep",
"POST /api/v1/admin/sweep",
- "GET /api/v1/admin/import-holds"
+ "GET /api/v1/admin/import-holds",
+ "GET /api/v1/usage"
],
],
"inputSchema": "billing.admin-request@1",
"inputSchema": "billing.admin-request@1",
"outputSchema": "billing.admin-result@1"
"outputSchema": "billing.admin-result@1"
new file mode 100644
new file mode 100644
@@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
+{
+ "id": "billing.usage.record",
+ "version": "1.0",
+ "owner": "billing",
+ "status": "active",
+ "visibility": "internal",
+ "description": "Record a platform.usage-sample@1 reading without moving money. The reading's idempotency_key deduplicates retries. Listing all readings requires billing.ledger.admin.",
+ "permissions": [
+ "billing:usage"
+ ],
+ "resourceConstraints": [
+ "none"
+ ],
+ "quotaClass": "ledger-write",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/usage"
+ ],
+ "inputSchema": "platform.usage-sample@1",
+ "outputSchema": "billing.usage-record-result@1"
+}
@@ -27,7 +27,8 @@
@@ -27,7 +27,8 @@
"billing.cashout.manage",
"billing.cashout.manage",
"billing.subscription.manage",
"billing.subscription.manage",
"billing.entitlement.check",
"billing.entitlement.check",
- "billing.ledger.admin"
+ "billing.ledger.admin",
+ "billing.usage.record"
],
],
"eventsProduced": [
"eventsProduced": [
"billing.transaction.settled",
"billing.transaction.settled",
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.84.0",
+ "version": "0.85.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.84.0",
+ "version": "0.85.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.84.0",
+ "version": "0.85.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",