openvibers/openvibe.contracts Commits

billing.money-reason@1: plain words for a ledger key (#409)

OP OpenVibers committed 3 days ago · e41722ed20 · 64 files, +479 −97

* billing.money-reason@1: one list of plain words for a ledger key

* Merge main; money reasons at 0.392.0

* Regenerate openapi after merge

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex b8bd24c1..612733a1 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -1,9 +1,13 @@
++# Changelog
++
++## 0.392.0 — 2026-11-10
++
++- Add `billing.money-reason@1` and `lib/money-reasons.js`: one list of the plain words for a movement of OpenCoins, Vibes or Balance. Each kind declares the key it matches (`quest:publish-a-video`, `Media request: <title>`, Billing's type:metadata), its site, what its subject is (a quest, a stream, a tip, a creator, an app, a project, a purchase), the phrase that names it ("for the quest {name}"), and how the subject's own name and address are found. `describe()` answers `{ key, text, subject: { name, url } | null }`, so a page writes "+15 OpenCoins" before the text and links it; an undeclared key reads "for something on <site>" with `known: false` and never shows the key. Fixtures cover a quest, a tip, a reason with no page and an undeclared key.
++
## 0.391.0 — 2026-11-09
- One Network experience curve, named reasons, level reads and capped service awards.
--# Changelog
--
## 0.390.0 — 2026-11-10
- A service manifest's `site` block takes an optional `strip`: up to three items a site adds to the network's footer strip, read with the rest of it at OpenVibe.Network `GET /api/frame/online?site=<id>`. An item is a plain label, an icon from openvibe-shared's glyph set, an https link on an `openvibe.*` domain, and either a `count` the site reports or a fixed `line` — never both. Live, Community and Games declare one each as the worked examples.
ddiff --git a/contracts/billing/money-reason.v1.json b/contracts/billing/money-reason.v1.json
nnew file mode 100644
iindex 00000000..0f21fed1
---- /dev/null
++++ b/contracts/billing/money-reason.v1.json
@@@ -0,0 +1,59 @@
++{
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/billing/money-reason.v1.json",
++ "title": "BillingMoneyReason",
++ "description": "billing.money-reason@1: why a movement of OpenCoins, Vibes or Balance happened, in the words a person reads. `key` is the raw ledger reason (OpenCoins reason, or Billing's type:metadata); `text` is the phrase without the amount (\"for the quest Publish a video\"), so a page writes \"+15 OpenCoins\" before it; `subject` is the thing it was for and where to read it, when there is one. A key nobody declared reads \"for something on <site>\", has known: false, and is never shown as its key. Same reason kinds, one list: openvibe-contracts/lib/money-reasons.js.",
++ "type": "object",
++ "required": [
++ "key",
++ "text"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "key": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "The ledger's own reason, kept for support and logs. A page never prints it."
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 300,
++ "description": "The plain phrase, without the amount: \"for the quest Publish a video\"."
++ },
++ "subject": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "description": "What the movement was for (a quest, a stream, a tip, a creator, an app, a project, a purchase), with its own name and address. Null when the reason has no page of its own.",
++ "required": [
++ "name"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "name": {
++ "type": "string",
++ "maxLength": 160
++ },
++ "url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri",
++ "maxLength": 500
++ }
++ }
++ },
++ "site": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "The site the movement belongs to, in the words a person reads (OpenVibe.Quest, OpenVibe.Live, OpenVibe)."
++ },
++ "known": {
++ "type": "boolean",
++ "description": "False for a key no kind declares; its text says \"for something on <site>\"."
++ }
++ }
++}
ddiff --git a/contracts/catalog.json b/contracts/catalog.json
iindex 652b2d0e..e4de54b1 100644
---- a/contracts/catalog.json
++++ b/contracts/catalog.json
@@@ -2574,6 +2574,15 @@
"compatibility": "backward",
"status": "active"
},
++ {
++ "id": "billing.money-reason",
++ "version": "1.0.0",
++ "owner": "billing",
++ "schema": "billing/money-reason.v1.json",
++ "visibility": "public",
++ "compatibility": "backward",
++ "status": "active"
++ },
{
"id": "billing.vibes-charge",
"version": "1.0.0",
ddiff --git a/fixtures/billing.money-reason/invalid/extra-field.json b/fixtures/billing.money-reason/invalid/extra-field.json
nnew file mode 100644
iindex 00000000..eabee7b3
---- /dev/null
++++ b/fixtures/billing.money-reason/invalid/extra-field.json
@@@ -0,0 +1 @@
++{ "key": "quest:go-live", "text": "for the quest Go live", "amount": 15 }
ddiff --git a/fixtures/billing.money-reason/invalid/key-null.json b/fixtures/billing.money-reason/invalid/key-null.json
nnew file mode 100644
iindex 00000000..607b9d2e
---- /dev/null
++++ b/fixtures/billing.money-reason/invalid/key-null.json
@@@ -0,0 +1 @@
++{ "key": null, "text": "for the quest Go live" }
ddiff --git a/fixtures/billing.money-reason/invalid/missing-text.json b/fixtures/billing.money-reason/invalid/missing-text.json
nnew file mode 100644
iindex 00000000..c18f9a38
---- /dev/null
++++ b/fixtures/billing.money-reason/invalid/missing-text.json
@@@ -0,0 +1 @@
++{ "key": "quest:go-live" }
ddiff --git a/fixtures/billing.money-reason/valid/no-subject.json b/fixtures/billing.money-reason/valid/no-subject.json
nnew file mode 100644
iindex 00000000..44ddb1d5
---- /dev/null
++++ b/fixtures/billing.money-reason/valid/no-subject.json
@@@ -0,0 +1,7 @@
++{
++ "key": "account_merge",
++ "text": "for merging your accounts",
++ "subject": null,
++ "site": "OpenVibe.Network",
++ "known": true
++}
ddiff --git a/fixtures/billing.money-reason/valid/quest.json b/fixtures/billing.money-reason/valid/quest.json
nnew file mode 100644
iindex 00000000..89697336
---- /dev/null
++++ b/fixtures/billing.money-reason/valid/quest.json
@@@ -0,0 +1,7 @@
++{
++ "key": "quest:publish-a-video",
++ "text": "for the quest Publish a video",
++ "subject": { "name": "Publish a video", "url": "https://openvibe.quest/quests/publish-a-video" },
++ "site": "OpenVibe.Quest",
++ "known": true
++}
ddiff --git a/fixtures/billing.money-reason/valid/tip.json b/fixtures/billing.money-reason/valid/tip.json
nnew file mode 100644
iindex 00000000..de27facb
---- /dev/null
++++ b/fixtures/billing.money-reason/valid/tip.json
@@@ -0,0 +1,7 @@
++{
++ "key": "donation",
++ "text": "for a tip to @alice",
++ "subject": { "name": "@alice", "url": "https://openvibe.live/@alice" },
++ "site": "OpenVibe",
++ "known": true
++}
ddiff --git a/fixtures/billing.money-reason/valid/undeclared.json b/fixtures/billing.money-reason/valid/undeclared.json
nnew file mode 100644
iindex 00000000..f4379884
---- /dev/null
++++ b/fixtures/billing.money-reason/valid/undeclared.json
@@@ -0,0 +1,7 @@
++{
++ "key": "nobody:declared-this",
++ "text": "for something on OpenVibe.Live",
++ "subject": null,
++ "site": "OpenVibe.Live",
++ "known": false
++}
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex 60375da0..440f4793 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -28684,6 +28684,70 @@
]
}
},
++ "billing.money-reason": {
++ "version": "1.0.0",
++ "owner": "billing",
++ "status": "active",
++ "schema": {
++ "$schema": "https://json-schema.org/draft/2020-12/schema",
++ "$id": "https://openvibe.network/contracts/billing/money-reason.v1.json",
++ "title": "BillingMoneyReason",
++ "description": "billing.money-reason@1: why a movement of OpenCoins, Vibes or Balance happened, in the words a person reads. `key` is the raw ledger reason (OpenCoins reason, or Billing's type:metadata); `text` is the phrase without the amount (\"for the quest Publish a video\"), so a page writes \"+15 OpenCoins\" before it; `subject` is the thing it was for and where to read it, when there is one. A key nobody declared reads \"for something on <site>\", has known: false, and is never shown as its key. Same reason kinds, one list: openvibe-contracts/lib/money-reasons.js.",
++ "type": "object",
++ "required": [
++ "key",
++ "text"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "key": {
++ "type": "string",
++ "maxLength": 200,
++ "description": "The ledger's own reason, kept for support and logs. A page never prints it."
++ },
++ "text": {
++ "type": "string",
++ "minLength": 1,
++ "maxLength": 300,
++ "description": "The plain phrase, without the amount: \"for the quest Publish a video\"."
++ },
++ "subject": {
++ "type": [
++ "object",
++ "null"
++ ],
++ "description": "What the movement was for (a quest, a stream, a tip, a creator, an app, a project, a purchase), with its own name and address. Null when the reason has no page of its own.",
++ "required": [
++ "name"
++ ],
++ "additionalProperties": false,
++ "properties": {
++ "name": {
++ "type": "string",
++ "maxLength": 160
++ },
++ "url": {
++ "type": [
++ "string",
++ "null"
++ ],
++ "format": "uri",
++ "maxLength": 500
++ }
++ }
++ },
++ "site": {
++ "type": "string",
++ "maxLength": 80,
++ "description": "The site the movement belongs to, in the words a person reads (OpenVibe.Quest, OpenVibe.Live, OpenVibe)."
++ },
++ "known": {
++ "type": "boolean",
++ "description": "False for a key no kind declares; its text says \"for something on <site>\"."
++ }
++ }
++ }
++ },
"billing.vibes-charge": {
"version": "1.0.0",
"owner": "billing",
ddiff --git a/generated/openapi/actor.json b/generated/openapi/actor.json
iindex aa4a3f67..38947f9b 100644
---- a/generated/openapi/actor.json
++++ b/generated/openapi/actor.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Actor API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/ai.json b/generated/openapi/ai.json
iindex b0484e10..bdffbe4a 100644
---- a/generated/openapi/ai.json
++++ b/generated/openapi/ai.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.AI API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/bench.json b/generated/openapi/bench.json
iindex 54c72fb3..5f29d506 100644
---- a/generated/openapi/bench.json
++++ b/generated/openapi/bench.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bench API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/billing.json b/generated/openapi/billing.json
iindex bd10b291..d5305b97 100644
---- a/generated/openapi/billing.json
++++ b/generated/openapi/billing.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Billing API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/blog.json b/generated/openapi/blog.json
iindex cd66653a..1e4f3f8a 100644
---- a/generated/openapi/blog.json
++++ b/generated/openapi/blog.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Blog API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/bot.json b/generated/openapi/bot.json
iindex 7f5366a5..f1d5e614 100644
---- a/generated/openapi/bot.json
++++ b/generated/openapi/bot.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Bot API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/chat.json b/generated/openapi/chat.json
iindex b2585cf5..961e5139 100644
---- a/generated/openapi/chat.json
++++ b/generated/openapi/chat.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Chat API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/codes.json b/generated/openapi/codes.json
iindex a7e8f552..cdf1c12f 100644
---- a/generated/openapi/codes.json
++++ b/generated/openapi/codes.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Codes API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/community.json b/generated/openapi/community.json
iindex b0d585ff..8d92c607 100644
---- a/generated/openapi/community.json
++++ b/generated/openapi/community.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Community API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/connect.json b/generated/openapi/connect.json
iindex 1f9caeee..6f340690 100644
---- a/generated/openapi/connect.json
++++ b/generated/openapi/connect.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe Connect API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/coupons.json b/generated/openapi/coupons.json
iindex 1c2c2ab5..9963e07a 100644
---- a/generated/openapi/coupons.json
++++ b/generated/openapi/coupons.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Coupons API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/deals.json b/generated/openapi/deals.json
iindex cb9fe1e6..5da78772 100644
---- a/generated/openapi/deals.json
++++ b/generated/openapi/deals.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Deals API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/design.json b/generated/openapi/design.json
iindex 64951c68..4f77df61 100644
---- a/generated/openapi/design.json
++++ b/generated/openapi/design.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Design API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/domains.json b/generated/openapi/domains.json
iindex ea84023b..5a4ffdf5 100644
---- a/generated/openapi/domains.json
++++ b/generated/openapi/domains.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Domains API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/events.json b/generated/openapi/events.json
iindex fd1fd3b9..dd1f8806 100644
---- a/generated/openapi/events.json
++++ b/generated/openapi/events.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Events API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/exchange.json b/generated/openapi/exchange.json
iindex 83cddde0..9053a2c2 100644
---- a/generated/openapi/exchange.json
++++ b/generated/openapi/exchange.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Exchange API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Exchange as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Exchange as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/food.json b/generated/openapi/food.json
iindex b220805f..42593b4f 100644
---- a/generated/openapi/food.json
++++ b/generated/openapi/food.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Food API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/games.json b/generated/openapi/games.json
iindex 29766c9c..7b53ba38 100644
---- a/generated/openapi/games.json
++++ b/generated/openapi/games.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Games API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/git.json b/generated/openapi/git.json
iindex b85b6554..6f94945c 100644
---- a/generated/openapi/git.json
++++ b/generated/openapi/git.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Git API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Git as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Git as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/host.json b/generated/openapi/host.json
iindex 011061c8..d940752d 100644
---- a/generated/openapi/host.json
++++ b/generated/openapi/host.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Host API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/inventory.json b/generated/openapi/inventory.json
iindex 8750215d..34cebda6 100644
---- a/generated/openapi/inventory.json
++++ b/generated/openapi/inventory.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Inventory API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/live.json b/generated/openapi/live.json
iindex df509bbc..e44c4bf5 100644
---- a/generated/openapi/live.json
++++ b/generated/openapi/live.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Live API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/mail.json b/generated/openapi/mail.json
iindex 65dd9746..f3e34c41 100644
---- a/generated/openapi/mail.json
++++ b/generated/openapi/mail.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Mail API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/media-hub.json b/generated/openapi/media-hub.json
iindex 4c9245e7..c14a1562 100644
---- a/generated/openapi/media-hub.json
++++ b/generated/openapi/media-hub.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.MediaHub API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/media.json b/generated/openapi/media.json
iindex 79246009..1b496a9d 100644
---- a/generated/openapi/media.json
++++ b/generated/openapi/media.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Media API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/network.json b/generated/openapi/network.json
iindex 7ebd3140..6c4a0f11 100644
---- a/generated/openapi/network.json
++++ b/generated/openapi/network.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Network API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/news.json b/generated/openapi/news.json
iindex 20b86cf7..bad2201e 100644
---- a/generated/openapi/news.json
++++ b/generated/openapi/news.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.News API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/openre.json b/generated/openapi/openre.json
iindex 1524e19a..74d6733c 100644
---- a/generated/openapi/openre.json
++++ b/generated/openapi/openre.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenRestream API",
-- "version": "0.391.0",
-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/play.json b/generated/openapi/play.json
iindex 4cf8927f..4f301690 100644
---- a/generated/openapi/play.json
++++ b/generated/openapi/play.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Play API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/quest.json b/generated/openapi/quest.json
iindex 71f0faa5..5877fab1 100644
---- a/generated/openapi/quest.json
++++ b/generated/openapi/quest.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Quest API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/report.json b/generated/openapi/report.json
iindex 7c5e4cda..416d74fe 100644
---- a/generated/openapi/report.json
++++ b/generated/openapi/report.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Report API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/reviews.json b/generated/openapi/reviews.json
iindex e25713ed..3a86026f 100644
---- a/generated/openapi/reviews.json
++++ b/generated/openapi/reviews.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Reviews API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/run.json b/generated/openapi/run.json
iindex 60a7aaed..fad08483 100644
---- a/generated/openapi/run.json
++++ b/generated/openapi/run.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Run API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/search.json b/generated/openapi/search.json
iindex af14a2d9..55a14012 100644
---- a/generated/openapi/search.json
++++ b/generated/openapi/search.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Search API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/services.json b/generated/openapi/services.json
iindex e5bf4775..77042f81 100644
---- a/generated/openapi/services.json
++++ b/generated/openapi/services.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Services API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/sources.json b/generated/openapi/sources.json
iindex e6707778..39b2c278 100644
---- a/generated/openapi/sources.json
++++ b/generated/openapi/sources.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Sources API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/space.json b/generated/openapi/space.json
iindex 41e6fa00..4a76fbec 100644
---- a/generated/openapi/space.json
++++ b/generated/openapi/space.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Space API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/tips.json b/generated/openapi/tips.json
iindex 23b344a5..68bf92d9 100644
---- a/generated/openapi/tips.json
++++ b/generated/openapi/tips.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tips API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/today.json b/generated/openapi/today.json
iindex 115b33df..3b9c9162 100644
---- a/generated/openapi/today.json
++++ b/generated/openapi/today.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Today API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/tools.json b/generated/openapi/tools.json
iindex 687e03bb..2ffa06cb 100644
---- a/generated/openapi/tools.json
++++ b/generated/openapi/tools.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Tools API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/trade.json b/generated/openapi/trade.json
iindex 1306d666..14174efa 100644
---- a/generated/openapi/trade.json
++++ b/generated/openapi/trade.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Trade API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/vip.json b/generated/openapi/vip.json
iindex 183fc053..223fd1a1 100644
---- a/generated/openapi/vip.json
++++ b/generated/openapi/vip.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.VIP API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/vision.json b/generated/openapi/vision.json
iindex 16db936f..c7af361a 100644
---- a/generated/openapi/vision.json
++++ b/generated/openapi/vision.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Vision API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/watch.json b/generated/openapi/watch.json
iindex 45c37a09..4509a9a8 100644
---- a/generated/openapi/watch.json
++++ b/generated/openapi/watch.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Watch API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/wiki.json b/generated/openapi/wiki.json
iindex 2adedb82..af097cf2 100644
---- a/generated/openapi/wiki.json
++++ b/generated/openapi/wiki.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Wiki API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/work.json b/generated/openapi/work.json
iindex b2f2b867..159cb710 100644
---- a/generated/openapi/work.json
++++ b/generated/openapi/work.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Work API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/openapi/zone.json b/generated/openapi/zone.json
iindex 8c9f1dbf..189152e7 100644
---- a/generated/openapi/zone.json
++++ b/generated/openapi/zone.json
@@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"title": "OpenVibe.Zone API",
-- "version": "0.391.0",
-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.391.0): each operation lists the capabilities it performs; a token for 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.392.0",
++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.392.0): each operation lists the capabilities it performs; a token for 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"
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex beadfe9d..255e2d32 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -174535,6 +174535,36 @@ export type BillingBalance = {
}
);
++/** [email protected] (owner: billing) */
++/**
++ * billing.money-reason@1: why a movement of OpenCoins, Vibes or Balance happened, in the words a person reads. `key` is the raw ledger reason (OpenCoins reason, or Billing's type:metadata); `text` is the phrase without the amount ("for the quest Publish a video"), so a page writes "+15 OpenCoins" before it; `subject` is the thing it was for and where to read it, when there is one. A key nobody declared reads "for something on <site>", has known: false, and is never shown as its key. Same reason kinds, one list: openvibe-contracts/lib/money-reasons.js.
++ */
++export interface BillingMoneyReason {
++ /**
++ * The ledger's own reason, kept for support and logs. A page never prints it.
++ */
++ key: string;
++ /**
++ * The plain phrase, without the amount: "for the quest Publish a video".
++ */
++ text: string;
++ /**
++ * What the movement was for (a quest, a stream, a tip, a creator, an app, a project, a purchase), with its own name and address. Null when the reason has no page of its own.
++ */
++ subject?: {
++ name: string;
++ url?: string | null;
++ } | null;
++ /**
++ * The site the movement belongs to, in the words a person reads (OpenVibe.Quest, OpenVibe.Live, OpenVibe).
++ */
++ site?: string;
++ /**
++ * False for a key no kind declares; its text says "for something on <site>".
++ */
++ known?: boolean;
++}
++
/** [email protected] (owner: billing) */
/**
* billing.vibes-charge@1: one Vibes Pay charge (D12). An app creates it for a person to pay with Vibes; GET /api/v1/vibes/charges/:id answers it, and a replay of the same app reference returns the same charge. amount is a whole number of Vibes (never a fraction, never nano-USD). status moves pending -> paid -> refunded, or pending -> expired|cancelled. sandbox charges use test Vibes only, worth nothing. The paid and refunded webhook/event payloads carry the same fields plus an opaque payer_ref and no person id or email.
ddiff --git a/index.js b/index.js
iindex 934cbe59..b5542c10 100644
---- a/index.js
++++ b/index.js
@@@ -32,5 +32,6 @@ module.exports = {
resources: require('./lib/resources'),
staff: require('./lib/staff'),
labels: require('./lib/labels'),
++ moneyReasons: require('./lib/money-reasons'),
openapi: require('./lib/openapi'),
};
ddiff --git a/lib/money-reasons.js b/lib/money-reasons.js
nnew file mode 100644
iindex 00000000..44a2f31a
---- /dev/null
++++ b/lib/money-reasons.js
@@@ -0,0 +1,142 @@
++'use strict';
++/**
++ * Money reasons: the plain words for a ledger row (2026-11-10).
++ *
++ * A ledger holds a key, never a sentence: OpenCoins rows carry `reason` ("quest:publish-a-video"),
++ * Vibes/Balance rows carry a type and metadata. This list is the one place each kind is declared:
++ * the key it matches, the site it belongs to, what its subject is (a quest, a stream, a tip, a
++ * creator, an app, a project, a purchase), the phrase that names it, and how the subject's own
++ * name and address are found.
++ *
++ * `describe(key, options)` makes `{ key, text, subject: { name, url } | null }`. `text` is the
++ * phrase without the amount ("for the quest Publish a video"); the page writes
++ * "+15 OpenCoins" before it and links the phrase to subject.url. A key nobody declared answers
++ * "for something on <site>" and is marked known: false, so a caller logs it once and never shows
++ * the key itself.
++ *
++ * `options.resolve` maps a subject type to a lookup: `{ quest: (id) => ({ name, url }) }`.
++ * `options.subject` is an already-found subject (a tip's creator, a project). `options.site`
++ * names the site for an undeclared key.
++ */
++
++/** The sites a key can belong to, in the words a person reads. */
++const SITES = Object.freeze({
++ quest: 'OpenVibe.Quest',
++ fun: 'OpenVibe.Fun',
++ live: 'OpenVibe.Live',
++ network: 'OpenVibe.Network',
++ billing: 'OpenVibe',
++ inventory: 'OpenVibe.Inventory',
++});
++
++/**
++ * One entry per reason kind.
++ * match RegExp whose first group is the subject's id or name (or an exact key string)
++ * site SITES key, for an undeclared key and for callers that show provenance
++ * subject the subject type `options.resolve` can look up; omitted when there is none
++ * text the phrase; `{name}` is where the subject's name goes
++ * fallback the name when the subject cannot be found ("a creator", "an item")
++ * path the subject's address; `{id}` is the matched group
++ * catalog where the subject's own names live (a declaration for the resolver)
++ */
++const KINDS = Object.freeze([
++ // ── OpenCoins: Network's coin_transactions.reason ───────────────────────────────────────────
++ Object.freeze({ id: 'quest', match: /^quest:([a-z0-9-]+)$/i, site: 'quest', subject: 'quest', text: 'for the quest {name}',
++ path: 'https://openvibe.quest/quests/{id}', catalog: 'https://openvibe.quest/api/v1/quests' }),
++ Object.freeze({ id: 'fun-quest', match: /^fun:([a-z0-9-]+)$/i, site: 'fun', subject: 'fun-quest', text: 'for the quest {name}',
++ path: 'https://openvibe.fun/q/{id}', catalog: 'https://openvibe.fun/api/v1/quests' }),
++ Object.freeze({ id: 'media-request', match: /^Media request: (.+)$/, site: 'live', subject: 'media', text: 'for the stream {name}' }),
++ Object.freeze({ id: 'media-refund', match: /^Refund: (.+)$/, site: 'live', subject: 'media', text: 'for a refund of {name}' }),
++ Object.freeze({ id: 'migration', match: 'migration:hobo-coins', site: 'network', text: 'for OpenCoins moved from the old Live wallet' }),
++ Object.freeze({ id: 'account-merge', match: 'account_merge', site: 'network', text: 'for merging your accounts' }),
++ Object.freeze({ id: 'account-deleted', match: 'account_deleted', site: 'network', text: 'for closing your account' }),
++ Object.freeze({ id: 'watch', match: 'watch', site: 'live', text: 'for watching a stream' }),
++ Object.freeze({ id: 'chat', match: 'chat', site: 'live', text: 'for chatting on a stream' }),
++ Object.freeze({ id: 'follow', match: 'follow', site: 'live', text: 'for following a channel' }),
++ Object.freeze({ id: 'bonus-game', match: 'bonus game', site: 'live', text: 'for winning the bonus game' }),
++ Object.freeze({ id: 'admin-grant', match: 'Admin grant', site: 'network', text: 'for OpenCoins from OpenVibe' }),
++
++ // ── Vibes / Balance: Billing's transaction type + metadata ──────────────────────────────────
++ Object.freeze({ id: 'ai-usage', match: 'credit:settle', site: 'billing', text: 'for AI usage' }),
++ Object.freeze({ id: 'topup', match: 'credit:topup', site: 'billing', text: 'for adding money to your Balance' }),
++ Object.freeze({ id: 'vibes-from-balance', match: 'purchase:vibes_from_balance', site: 'billing', text: 'for buying Vibes with your Balance' }),
++ Object.freeze({ id: 'vibes-bought', match: 'purchase:vibes', site: 'billing', text: 'for buying Vibes' }),
++ Object.freeze({ id: 'store-purchase', match: 'purchase:store', site: 'inventory', subject: 'item', text: 'for buying {name}', fallback: 'an item' }),
++ Object.freeze({ id: 'app-payment', match: 'vibes_charge', site: 'billing', subject: 'app', text: 'for paying {name}', fallback: 'an app' }),
++ Object.freeze({ id: 'app-purchase', match: 'purchase:app', site: 'billing', subject: 'app', text: 'for paying {name}', fallback: 'an app' }),
++ Object.freeze({ id: 'tip', match: 'donation', site: 'billing', subject: 'creator', text: 'for a tip to {name}', fallback: 'a creator' }),
++ Object.freeze({ id: 'subscription', match: 'subscription', site: 'billing', subject: 'creator', text: 'for a subscription to {name}', fallback: 'a creator' }),
++ Object.freeze({ id: 'subscription-share', match: 'subscription_share', site: 'billing', subject: 'creator', text: 'for a subscription to {name}', fallback: 'a creator' }),
++ Object.freeze({ id: 'refund', match: 'refund', site: 'billing', text: 'for a refund' }),
++ Object.freeze({ id: 'vibes-refund', match: 'vibes_refund', site: 'billing', text: 'for a refund of Vibes' }),
++ Object.freeze({ id: 'move-to-project', match: 'balance_transfer', site: 'billing', subject: 'project', text: 'for moving money to {name}', fallback: 'a project' }),
++ Object.freeze({ id: 'cashout-request', match: 'cashout_request', site: 'billing', text: 'for cashing out' }),
++ Object.freeze({ id: 'cashout-paid', match: 'cashout_paid', site: 'billing', text: 'for a payout' }),
++ Object.freeze({ id: 'cashout-denied', match: 'cashout_denied', site: 'billing', text: 'for a cashout that was denied' }),
++ Object.freeze({ id: 'recycle', match: 'recycle', site: 'billing', text: 'for moving your cashout balance back to Vibes' }),
++ Object.freeze({ id: 'adjustment', match: 'adjustment', site: 'billing', text: 'for an adjustment by OpenVibe' }),
++]);
++
++/** "live-setup-slot" → "Live setup slot": the name a slug gives when its catalog is not at hand. */
++function titleFromSlug(slug) {
++ const words = String(slug == null ? '' : slug).replace(/[-_]+/g, ' ').trim();
++ return words ? words.charAt(0).toUpperCase() + words.slice(1) : '';
++}
++
++function siteName(id) {
++ return SITES[String(id || '').toLowerCase()] || 'OpenVibe';
++}
++
++function subjectFrom(value, kind, options) {
++ if (options.subject && typeof options.subject === 'object' && options.subject.name) {
++ return { name: String(options.subject.name).slice(0, 160), url: options.subject.url ? String(options.subject.url).slice(0, 500) : null };
++ }
++ let found = null;
++ if (value != null && kind.subject && options.resolve && typeof options.resolve[kind.subject] === 'function') {
++ try { found = options.resolve[kind.subject](value); } catch { found = null; }
++ }
++ if (typeof found === 'string') found = { name: found };
++ const name = found && found.name ? String(found.name).slice(0, 160) : (kind.fallback || titleFromSlug(value));
++ const url = found && found.url ? String(found.url).slice(0, 500) : (kind.path && value != null ? kind.path.replace('{id}', encodeURIComponent(value)) : null);
++ if (!name) return null;
++ return { name, url: url || null };
++}
++
++/**
++ * The plain words for a ledger reason.
++ * @returns {{ key: string, text: string, subject: { name: string, url: string|null }|null, site: string, known: boolean }}
++ */
++function describe(key, options = {}) {
++ const raw = key == null ? '' : String(key).trim();
++ let kind = null;
++ let value = null;
++ for (const entry of KINDS) {
++ if (typeof entry.match === 'string') {
++ if (raw === entry.match) { kind = entry; break; }
++ continue;
++ }
++ const m = raw.match(entry.match);
++ if (m) { kind = entry; value = m[1] == null ? null : m[1].trim(); break; }
++ }
++ if (!kind) {
++ return { key: raw, text: `for something on ${siteName(options.site || raw.split(':')[0])}`, subject: null,
++ site: siteName(options.site || raw.split(':')[0]), known: false };
++ }
++ const subject = kind.subject ? subjectFrom(value, kind, options) : null;
++ const text = kind.text.indexOf('{name}') < 0 ? kind.text
++ : kind.text.replace('{name}', subject ? subject.name : (kind.fallback || 'something'));
++ return { key: raw, text, subject, site: siteName(kind.site), known: true };
++}
++
++/** A row from a service that keeps the raw key: `{ reason, app_id }` or `{ reason }`. */
++function describeRow(row, options = {}) {
++ const reason = row && (row.reason != null ? row.reason : row.reason_key);
++ return describe(reason, { ...options, site: options.site || (row && (row.app_id || row.site)) || undefined });
++}
++
++/** What an undeclared key reads, for a test and for a caller that wants only the words. */
++function unknownText(site) {
++ return `for something on ${siteName(site)}`;
++}
++
++module.exports = { KINDS, SITES, describe, describeRow, unknownText, siteName, titleFromSlug };
ddiff --git a/package.json b/package.json
iindex 72af731b..f70eafac 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.391.0",
++ "version": "0.392.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",
ddiff --git a/test/money-reasons.test.js b/test/money-reasons.test.js
nnew file mode 100644
iindex 00000000..a1c2a6a0
---- /dev/null
++++ b/test/money-reasons.test.js
@@@ -0,0 +1,41 @@
++'use strict';
++// billing.money-reason@1 and lib/money-reasons.js: one list of plain words for a ledger key.
++const assert = require('assert');
++const contracts = require('..');
++
++let n = 0;
++const ok = (cond, msg) => { assert.ok(cond, msg); n++; };
++const { moneyReasons } = contracts;
++
++const quest = moneyReasons.describe('quest:publish-a-video', { resolve: { quest: () => ({ name: 'Publish a video', url: 'https://openvibe.quest/quests/publish-a-video' }) } });
++ok(quest.known && quest.text === 'for the quest Publish a video', 'a quest key reads "for the quest Publish a video"');
++ok(quest.subject && quest.subject.url === 'https://openvibe.quest/quests/publish-a-video', 'and links to the quest');
++
++const slug = moneyReasons.describe('quest:publish-a-video');
++ok(slug.text === 'for the quest Publish a video', 'without the catalog, the slug names the quest');
++ok(slug.subject.url === 'https://openvibe.quest/quests/publish-a-video', 'and its address still holds');
++
++ok(moneyReasons.describe('Media request: A film').text === 'for the stream A film', 'a media request names the stream');
++ok(moneyReasons.describe('Refund: A film').text === 'for a refund of A film', 'a refund names what it returns');
++ok(moneyReasons.describe('account_merge').text === 'for merging your accounts', 'a reason with no page has its own words');
++ok(moneyReasons.describe('account_merge').subject === null, 'and no subject');
++
++const tip = moneyReasons.describe('donation', { subject: { name: '@alice', url: 'https://openvibe.live/@alice' } });
++ok(tip.text === 'for a tip to @alice' && tip.subject.url === 'https://openvibe.live/@alice', 'a provided subject names a tip');
++
++const unknown = moneyReasons.describe('nobody:declared-this', { site: 'live' });
++ok(!unknown.known, 'an undeclared key is not known');
++ok(unknown.text === 'for something on OpenVibe.Live', 'it reads "for something on <site>"');
++ok(!/nobody:|declared-this/.test(unknown.text), 'and never shows the key');
++
++const row = moneyReasons.describeRow({ reason: 'quest:go-live', app_id: 'quest' });
++ok(row.text === 'for the quest Go live' && row.site === 'OpenVibe.Quest', 'a raw row is described by its reason and app');
++
++const schema = contracts.schema('billing.money-reason@1');
++ok(schema && schema.properties.text.maxLength === 300, 'billing.money-reason@1 is in the catalog');
++ok(contracts.validate('billing.money-reason@1', { key: 'quest:go-live', text: 'for the quest Go live', subject: { name: 'Go live', url: 'https://openvibe.quest/quests/go-live' }, site: 'OpenVibe.Quest', known: true }).valid,
++ 'a made reason validates');
++ok(!contracts.validate('billing.money-reason@1', { key: 'quest:go-live', text: 'for the quest Go live', extra: 1 }).valid,
++ 'a made reason has no extra fields');
++
++console.log(`openvibe-contracts money reasons: ${n} checks passed`);
ddiff --git a/test/run.js b/test/run.js
iindex 1540c993..d6c99828 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -39,6 +39,7 @@ function sub(file, ...args) {
sub(path.join(ROOT, 'test/link-brand.test.js'));
sub(path.join(ROOT, 'test/link-compute.test.js'));
++sub(path.join(ROOT, 'test/money-reasons.test.js'));
// ── Catalog integrity ────────────────────────────────────────────────────
const files = [];