openvibers/openvibe.contracts Commits
BA3: the latency classes and the batches contract (#344)
Changes
@@@ -1,4 +1,22 @@# Changelog++## 0.336.0 — 2026-11-07++++- The run's options carry the latency class the caller asks for: `interactive` (answer as soon as the provider can),++ `background` (minutes; the run is priced on the cheapest tier that can wait) or `batch` (hours; the run is queued and++ answered through a provider's own batch API). A batch run may also say `deadline_hours` (1 to 72): how long the answer may++ take. The class is the workflow's own (the route's `constraints.latency_class`): a caller may ask for a slower class and++ never for a faster one, and a faster ask is not refused — the run happens in the workflow's own class and says so.++- The run's answer carries `latency_class` (the class the run really ran in), `service_tier` (`standard`, `flex`,++ `batch`, `offpeak`, `local`: the tier it was priced on — the one the provider served or the one the router chose) and++ `latency_clamped` (the class asked for and the class used, when the ask was clamped).++- Add the batches contract: `ai.batch@1`, `ai.batch-item@1`, `ai.batch-create-request@1`, `ai.batch-create-result@1` and++ `ai.batch-read-result@1` for `/api/v1/batches` on OpenVibe.AI. A batch is a queue of many prompts that are answered++ later, at half the retail price; the batch says where it stands, and a prompt that reaches its deadline without cheap++ capacity expires and is never charged.++- Add the grants `ai.batches.create` (open a batch, cancel one) and `ai.batches.read` (read the caller's own batches).++ A first-party service opens a batch on the network's own account with `ai.batches.create`; a person or a developer app++ pays with its own account.++## 0.335.0 — 2026-11-07nnew file mode 100644@@@ -0,0 +1,55 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-create-request.v1.json",++ "title": "AiBatchCreateRequest",++ "description": "ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).",++ "type": "object",++ "required": [++ "items"++ ],++ "additionalProperties": false,++ "properties": {++ "items": {++ "type": "array",++ "minItems": 1,++ "description": "The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.",++ "items": {++ "anyOf": [++ {++ "type": "string",++ "minLength": 1,++ "maxLength": 20000++ },++ {++ "type": "object",++ "required": [++ "prompt"++ ],++ "additionalProperties": false,++ "properties": {++ "prompt": {++ "type": "string",++ "minLength": 1,++ "maxLength": 20000++ },++ "model": {++ "type": "string",++ "description": "The model this item wants (a model key, or provider/model)."++ }++ }++ }++ ]++ }++ },++ "model": {++ "type": "string",++ "description": "The model the whole batch asks for, when no item names one."++ },++ "deadline_hours": {++ "type": "integer",++ "minimum": 1,++ "maximum": 72,++ "description": "How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged."++ }++ }++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-create-result.v1.json",++ "title": "AiBatchCreateResult",++ "description": "ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).",++ "type": "object",++ "required": [++ "batch"++ ],++ "additionalProperties": false,++ "properties": {++ "batch": {++ "$ref": "batch.v1.json"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,136 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-item.v1.json",++ "title": "AiBatchItem",++ "description": "ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.",++ "type": "object",++ "required": [++ "idx",++ "status",++ "model",++ "run_id",++ "answer",++ "error_code",++ "error_detail",++ "reason",++ "capacity",++ "route_provider",++ "route_model",++ "deadline_at",++ "created_at",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "idx": {++ "type": "integer",++ "minimum": 0,++ "description": "The item's place in the batch."++ },++ "status": {++ "enum": [++ "queued",++ "waiting",++ "running",++ "succeeded",++ "failed",++ "expired",++ "cancelled"++ ],++ "description": "queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled)."++ },++ "model": {++ "type": [++ "string",++ "null"++ ],++ "description": "The model the item asked for, or the batch's own."++ },++ "run_id": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The run the item made, when it made one."++ },++ "answer": {++ "type": [++ "string",++ "null"++ ],++ "description": "The answer the provider gave, as text."++ },++ "error_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "error_detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "description": "Why the item ended without an answer (it expired, it was cancelled)."++ },++ "capacity": {++ "type": [++ "string",++ "null"++ ],++ "description": "Where the answer came from: a community offer, the regular capacity, or a provider's own batch API."++ },++ "route_provider": {++ "type": [++ "string",++ "null"++ ]++ },++ "route_model": {++ "type": [++ "string",++ "null"++ ]++ },++ "cost_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float."++ },++ "absorbed_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string."++ },++ "charged_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the customer was charged, in nano-USD, as a string: half the retail card."++ },++ "deadline_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,35 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-read-result.v1.json",++ "title": "AiBatchReadResult",++ "description": "ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "batches"++ ],++ "additionalProperties": false,++ "properties": {++ "batches": {++ "type": "array",++ "items": {++ "$ref": "batch.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "batch"++ ],++ "additionalProperties": false,++ "properties": {++ "batch": {++ "$ref": "batch.v1.json"++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,103 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch.v1.json",++ "title": "AiBatch",++ "description": "ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).",++ "type": "object",++ "required": [++ "id",++ "status",++ "owner_type",++ "owner_id",++ "project_id",++ "item_count",++ "items",++ "discount",++ "deadline_at",++ "created_at",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^bat_[0-9a-f]{24}$"++ },++ "status": {++ "enum": [++ "queued",++ "running",++ "succeeded",++ "failed",++ "expired",++ "cancelled"++ ],++ "description": "queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it)."++ },++ "owner_type": {++ "enum": [++ "user",++ "app",++ "service"++ ],++ "description": "Who pays for the batch."++ },++ "owner_id": {++ "type": "string"++ },++ "project_id": {++ "type": [++ "string",++ "null"++ ],++ "description": "The developer app's project, when an app owns the batch."++ },++ "item_count": {++ "type": "integer",++ "minimum": 0,++ "description": "How many prompts the batch carries."++ },++ "items": {++ "type": "array",++ "items": {++ "$ref": "batch-item.v1.json"++ },++ "description": "The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id."++ },++ "discount": {++ "type": "string",++ "description": "The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half)."++ },++ "deadline_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the batch stops waiting: the deadline the caller asked for (1 to 72 hours)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "absorbed_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the network absorbed for this batch, in nano-USD, as a string."++ },++ "ran_on_regular_capacity": {++ "type": "boolean",++ "description": "The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount."++ }++ }++}@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/ai/run-request.v1.json","title": "AiRunRequest",-- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",++ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).","type": "object","required": ["workflow"@@@ -51,6 +51,16 @@"type": "boolean","default": false,"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."++ },++ "latency": {++ "enum": ["interactive", "background", "batch"],++ "description": "The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped)."++ },++ "deadline_hours": {++ "type": "integer",++ "minimum": 1,++ "maximum": 72,++ "description": "For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default."}}},@@@ -2,7 +2,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/ai/run.v1.json","title": "AiRun",-- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",++ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.","type": "object","required": ["id", "status", "workflow", "template", "route", "requester", "on_behalf_of", "attribution", "source_service", "target", "input", "output", "error", "synthetic", "provenance", "usage", "citations_count", "retry_of", "idempotency_key", "trace_id", "created_at", "started_at", "finished_at"],"additionalProperties": false,@@@ -69,6 +69,25 @@}},"citations_count": { "type": "integer", "minimum": 0, "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later." },++ "latency_class": {++ "enum": ["interactive", "background", "batch"],++ "description": "The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows."++ },++ "service_tier": {++ "type": ["string", "null"],++ "enum": ["standard", "flex", "batch", "offpeak", "local", null],++ "description": "The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving."++ },++ "latency_clamped": {++ "type": ["object", "null"],++ "required": ["asked", "used"],++ "additionalProperties": false,++ "properties": {++ "asked": { "enum": ["interactive", "background", "batch"], "description": "The class the caller asked for." },++ "used": { "enum": ["interactive", "background", "batch"], "description": "The class the run really ran in." }++ },++ "description": "Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it."++ },"retry_of": { "type": ["string", "null"], "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$" },"idempotency_key": { "type": ["string", "null"] },"trace_id": { "type": ["string", "null"] },@@@ -1630,6 +1630,51 @@"compatibility": "backward","status": "active"},++ {++ "id": "ai.batch",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/batch.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.batch-item",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/batch-item.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.batch-create-request",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/batch-create-request.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.batch-create-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/batch-create-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "ai.batch-read-result",++ "version": "1.0.0",++ "owner": "ai",++ "schema": "ai/batch-read-result.v1.json",++ "visibility": "first-party",++ "compatibility": "backward",++ "status": "active"++ },{"id": "ai.run-read-result","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,6 @@++{++ "items": [++ "one prompt"++ ],++ "deadline_hours": 100++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "items": [++ ""++ ]++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "items": []++}nnew file mode 100644@@@ -0,0 +1,9 @@++{++ "items": [++ "one prompt",++ {++ "prompt": "another prompt",++ "model": "gpt-4o-mini"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,7 @@++{++ "items": [++ "one prompt"++ ],++ "model": "gpt-4o-mini",++ "deadline_hours": 24++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "batch_id": "bat_0123456789abcdef01234567"++}nnew file mode 100644@@@ -0,0 +1,38 @@++{++ "batch": {++ "id": "bat_0123456789abcdef01234567",++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++ }++}nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "idx": 0,++ "status": "later",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,76 @@++{++ "batch": {++ "id": "bat_0123456789abcdef01234567",++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++ },++ "batches": [++ {++ "id": "bat_0123456789abcdef01234567",++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++ }++ ]++}nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1,38 @@++{++ "batch": {++ "id": "bat_0123456789abcdef01234567",++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++ }++}nnew file mode 100644@@@ -0,0 +1,40 @@++{++ "batches": [++ {++ "id": "bat_0123456789abcdef01234567",++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,35 @@++{++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++}nnew file mode 100644@@@ -0,0 +1,36 @@++{++ "id": "bat_0123456789abcdef01234567",++ "status": "whenever",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++}nnew file mode 100644@@@ -0,0 +1,16 @@++{++ "id": "bat_0123456789abcdef01234567",++ "status": "queued",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 2,++ "items": [],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": null,++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++}nnew file mode 100644@@@ -0,0 +1,36 @@++{++ "id": "bat_0123456789abcdef01234567",++ "status": "succeeded",++ "owner_type": "user",++ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "project_id": null,++ "item_count": 1,++ "items": [++ {++ "idx": 0,++ "status": "succeeded",++ "model": "gpt-4o-mini",++ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",++ "answer": "A digest of the week.",++ "error_code": null,++ "error_detail": null,++ "reason": null,++ "capacity": "regular",++ "route_provider": "openai",++ "route_model": "gpt-4o-mini",++ "cost_nano_usd": "1200",++ "absorbed_nano_usd": "0",++ "charged_nano_usd": "600",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z"++ }++ ],++ "discount": "500000",++ "deadline_at": "2026-11-08T22:58:00.000Z",++ "created_at": "2026-11-07T22:58:00.000Z",++ "updated_at": "2026-11-08T00:10:00.000Z",++ "finished_at": "2026-11-08T00:10:00.000Z",++ "absorbed_nano_usd": "0",++ "ran_on_regular_capacity": false++}@@@ -15271,7 +15271,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/ai/run-request.v1.json","title": "AiRunRequest",-- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",++ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).","type": "object","required": ["workflow"@@@ -15320,6 +15320,20 @@"type": "boolean","default": false,"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."++ },++ "latency": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped)."++ },++ "deadline_hours": {++ "type": "integer",++ "minimum": 1,++ "maximum": 72,++ "description": "For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default."}}},@@@ -15348,7 +15362,7 @@"$schema": "https://json-schema.org/draft/2020-12/schema","$id": "https://openvibe.network/contracts/ai/run.v1.json","title": "AiRun",-- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",++ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.","type": "object","required": ["id",@@@ -15646,6 +15660,59 @@"minimum": 0,"description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."},++ "latency_class": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows."++ },++ "service_tier": {++ "type": [++ "string",++ "null"++ ],++ "enum": [++ "standard",++ "flex",++ "batch",++ "offpeak",++ "local",++ null++ ],++ "description": "The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving."++ },++ "latency_clamped": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "asked",++ "used"++ ],++ "additionalProperties": false,++ "properties": {++ "asked": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the caller asked for."++ },++ "used": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the run really ran in."++ }++ },++ "description": "Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it."++ },"retry_of": {"type": ["string",@@@ -18835,6 +18902,376 @@]}},++ "ai.batch": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch.v1.json",++ "title": "AiBatch",++ "description": "ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).",++ "type": "object",++ "required": [++ "id",++ "status",++ "owner_type",++ "owner_id",++ "project_id",++ "item_count",++ "items",++ "discount",++ "deadline_at",++ "created_at",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^bat_[0-9a-f]{24}$"++ },++ "status": {++ "enum": [++ "queued",++ "running",++ "succeeded",++ "failed",++ "expired",++ "cancelled"++ ],++ "description": "queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it)."++ },++ "owner_type": {++ "enum": [++ "user",++ "app",++ "service"++ ],++ "description": "Who pays for the batch."++ },++ "owner_id": {++ "type": "string"++ },++ "project_id": {++ "type": [++ "string",++ "null"++ ],++ "description": "The developer app's project, when an app owns the batch."++ },++ "item_count": {++ "type": "integer",++ "minimum": 0,++ "description": "How many prompts the batch carries."++ },++ "items": {++ "type": "array",++ "items": {++ "$ref": "batch-item.v1.json"++ },++ "description": "The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id."++ },++ "discount": {++ "type": "string",++ "description": "The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half)."++ },++ "deadline_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the batch stops waiting: the deadline the caller asked for (1 to 72 hours)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "absorbed_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the network absorbed for this batch, in nano-USD, as a string."++ },++ "ran_on_regular_capacity": {++ "type": "boolean",++ "description": "The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount."++ }++ }++ }++ },++ "ai.batch-item": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-item.v1.json",++ "title": "AiBatchItem",++ "description": "ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.",++ "type": "object",++ "required": [++ "idx",++ "status",++ "model",++ "run_id",++ "answer",++ "error_code",++ "error_detail",++ "reason",++ "capacity",++ "route_provider",++ "route_model",++ "deadline_at",++ "created_at",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "idx": {++ "type": "integer",++ "minimum": 0,++ "description": "The item's place in the batch."++ },++ "status": {++ "enum": [++ "queued",++ "waiting",++ "running",++ "succeeded",++ "failed",++ "expired",++ "cancelled"++ ],++ "description": "queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled)."++ },++ "model": {++ "type": [++ "string",++ "null"++ ],++ "description": "The model the item asked for, or the batch's own."++ },++ "run_id": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The run the item made, when it made one."++ },++ "answer": {++ "type": [++ "string",++ "null"++ ],++ "description": "The answer the provider gave, as text."++ },++ "error_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "error_detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "description": "Why the item ended without an answer (it expired, it was cancelled)."++ },++ "capacity": {++ "type": [++ "string",++ "null"++ ],++ "description": "Where the answer came from: a community offer, the regular capacity, or a provider's own batch API."++ },++ "route_provider": {++ "type": [++ "string",++ "null"++ ]++ },++ "route_model": {++ "type": [++ "string",++ "null"++ ]++ },++ "cost_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float."++ },++ "absorbed_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string."++ },++ "charged_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the customer was charged, in nano-USD, as a string: half the retail card."++ },++ "deadline_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ }++ },++ "ai.batch-create-request": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-create-request.v1.json",++ "title": "AiBatchCreateRequest",++ "description": "ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).",++ "type": "object",++ "required": [++ "items"++ ],++ "additionalProperties": false,++ "properties": {++ "items": {++ "type": "array",++ "minItems": 1,++ "description": "The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.",++ "items": {++ "anyOf": [++ {++ "type": "string",++ "minLength": 1,++ "maxLength": 20000++ },++ {++ "type": "object",++ "required": [++ "prompt"++ ],++ "additionalProperties": false,++ "properties": {++ "prompt": {++ "type": "string",++ "minLength": 1,++ "maxLength": 20000++ },++ "model": {++ "type": "string",++ "description": "The model this item wants (a model key, or provider/model)."++ }++ }++ }++ ]++ }++ },++ "model": {++ "type": "string",++ "description": "The model the whole batch asks for, when no item names one."++ },++ "deadline_hours": {++ "type": "integer",++ "minimum": 1,++ "maximum": 72,++ "description": "How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged."++ }++ }++ }++ },++ "ai.batch-create-result": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-create-result.v1.json",++ "title": "AiBatchCreateResult",++ "description": "ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).",++ "type": "object",++ "required": [++ "batch"++ ],++ "additionalProperties": false,++ "properties": {++ "batch": {++ "$ref": "batch.v1.json"++ }++ }++ }++ },++ "ai.batch-read-result": {++ "version": "1.0.0",++ "owner": "ai",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/ai/batch-read-result.v1.json",++ "title": "AiBatchReadResult",++ "description": "ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "batches"++ ],++ "additionalProperties": false,++ "properties": {++ "batches": {++ "type": "array",++ "items": {++ "$ref": "batch.v1.json"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "batch"++ ],++ "additionalProperties": false,++ "properties": {++ "batch": {++ "$ref": "batch.v1.json"++ }++ }++ }++ ]++ }++ },"ai.run-read-result": {"version": "1.0.0","owner": "ai",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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"@@@ -288,6 +288,228 @@}}},++ "/api/v1/batches": {++ "get": {++ "operationId": "get_api_v1_batches",++ "summary": "Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id).",++ "description": "**ai.batches.read** (first-party): Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id). Somebody else's batch is not found.",++ "tags": [++ "ai.batches"++ ],++ "x-openvibe-capabilities": [++ "ai.batches.read"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "ai-read",++ "security": [++ {++ "openvibeToken": [++ "ai.batches.read"++ ]++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/ai.batch-read-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_batches",++ "summary": "Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel).",++ "description": "**ai.batches.create** (first-party): Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel). A batch is answered later, through a provider's own batch API when there is one, and is billed at half the retail price. A first-party service needs this grant to open a batch on the network's own account; a person or a developer app pays with its own account.",++ "tags": [++ "ai.batches"++ ],++ "x-openvibe-capabilities": [++ "ai.batches.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "ai-batch",++ "security": [++ {++ "openvibeToken": [++ "ai.batches.create"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/ai.batch-create-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/ai.batch-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/batches/{id}": {++ "get": {++ "operationId": "get_api_v1_batches_id",++ "summary": "Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id).",++ "description": "**ai.batches.read** (first-party): Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id). Somebody else's batch is not found.",++ "tags": [++ "ai.batches"++ ],++ "x-openvibe-capabilities": [++ "ai.batches.read"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "ai-read",++ "security": [++ {++ "openvibeToken": [++ "ai.batches.read"++ ]++ }++ ],++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/ai.batch-read-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/batches/{id}/cancel": {++ "post": {++ "operationId": "post_api_v1_batches_id_cancel",++ "summary": "Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel).",++ "description": "**ai.batches.create** (first-party): Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel). A batch is answered later, through a provider's own batch API when there is one, and is billed at half the retail price. A first-party service needs this grant to open a batch on the network's own account; a person or a developer app pays with its own account.",++ "tags": [++ "ai.batches"++ ],++ "x-openvibe-capabilities": [++ "ai.batches.create"++ ],++ "x-openvibe-visibility": [++ "first-party"++ ],++ "x-openvibe-quota-class": "ai-batch",++ "security": [++ {++ "openvibeToken": [++ "ai.batches.create"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/ai.batch-create-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/ai.batch-create-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },"/api/v1/cache": {"delete": {"operationId": "delete_api_v1_cache",@@@ -4726,6 +4948,341 @@}}},++ "ai.batch-create-request.v1": {++ "title": "AiBatchCreateRequest",++ "description": "ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).",++ "type": "object",++ "required": [++ "items"++ ],++ "additionalProperties": false,++ "properties": {++ "items": {++ "type": "array",++ "minItems": 1,++ "description": "The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.",++ "items": {++ "anyOf": [++ {++ "type": "string",++ "minLength": 1,++ "maxLength": 20000++ },++ {++ "type": "object",++ "required": [++ "prompt"++ ],++ "additionalProperties": false,++ "properties": {++ "prompt": {++ "type": "string",++ "minLength": 1,++ "maxLength": 20000++ },++ "model": {++ "type": "string",++ "description": "The model this item wants (a model key, or provider/model)."++ }++ }++ }++ ]++ }++ },++ "model": {++ "type": "string",++ "description": "The model the whole batch asks for, when no item names one."++ },++ "deadline_hours": {++ "type": "integer",++ "minimum": 1,++ "maximum": 72,++ "description": "How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged."++ }++ }++ },++ "ai.batch-create-result.v1": {++ "title": "AiBatchCreateResult",++ "description": "ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).",++ "type": "object",++ "required": [++ "batch"++ ],++ "additionalProperties": false,++ "properties": {++ "batch": {++ "$ref": "#/components/schemas/ai.batch.v1"++ }++ }++ },++ "ai.batch-item.v1": {++ "title": "AiBatchItem",++ "description": "ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.",++ "type": "object",++ "required": [++ "idx",++ "status",++ "model",++ "run_id",++ "answer",++ "error_code",++ "error_detail",++ "reason",++ "capacity",++ "route_provider",++ "route_model",++ "deadline_at",++ "created_at",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "idx": {++ "type": "integer",++ "minimum": 0,++ "description": "The item's place in the batch."++ },++ "status": {++ "enum": [++ "queued",++ "waiting",++ "running",++ "succeeded",++ "failed",++ "expired",++ "cancelled"++ ],++ "description": "queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled)."++ },++ "model": {++ "type": [++ "string",++ "null"++ ],++ "description": "The model the item asked for, or the batch's own."++ },++ "run_id": {++ "type": [++ "string",++ "null"++ ],++ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$",++ "description": "The run the item made, when it made one."++ },++ "answer": {++ "type": [++ "string",++ "null"++ ],++ "description": "The answer the provider gave, as text."++ },++ "error_code": {++ "type": [++ "string",++ "null"++ ]++ },++ "error_detail": {++ "type": [++ "string",++ "null"++ ]++ },++ "reason": {++ "type": [++ "string",++ "null"++ ],++ "description": "Why the item ended without an answer (it expired, it was cancelled)."++ },++ "capacity": {++ "type": [++ "string",++ "null"++ ],++ "description": "Where the answer came from: a community offer, the regular capacity, or a provider's own batch API."++ },++ "route_provider": {++ "type": [++ "string",++ "null"++ ]++ },++ "route_model": {++ "type": [++ "string",++ "null"++ ]++ },++ "cost_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float."++ },++ "absorbed_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string."++ },++ "charged_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the customer was charged, in nano-USD, as a string: half the retail card."++ },++ "deadline_at": {++ "type": "string",++ "format": "date-time"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ }++ }++ },++ "ai.batch-read-result.v1": {++ "title": "AiBatchReadResult",++ "description": "ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.",++ "anyOf": [++ {++ "type": "object",++ "required": [++ "batches"++ ],++ "additionalProperties": false,++ "properties": {++ "batches": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/ai.batch.v1"++ }++ }++ }++ },++ {++ "type": "object",++ "required": [++ "batch"++ ],++ "additionalProperties": false,++ "properties": {++ "batch": {++ "$ref": "#/components/schemas/ai.batch.v1"++ }++ }++ }++ ]++ },++ "ai.batch.v1": {++ "title": "AiBatch",++ "description": "ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).",++ "type": "object",++ "required": [++ "id",++ "status",++ "owner_type",++ "owner_id",++ "project_id",++ "item_count",++ "items",++ "discount",++ "deadline_at",++ "created_at",++ "updated_at"++ ],++ "additionalProperties": false,++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^bat_[0-9a-f]{24}$"++ },++ "status": {++ "enum": [++ "queued",++ "running",++ "succeeded",++ "failed",++ "expired",++ "cancelled"++ ],++ "description": "queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it)."++ },++ "owner_type": {++ "enum": [++ "user",++ "app",++ "service"++ ],++ "description": "Who pays for the batch."++ },++ "owner_id": {++ "type": "string"++ },++ "project_id": {++ "type": [++ "string",++ "null"++ ],++ "description": "The developer app's project, when an app owns the batch."++ },++ "item_count": {++ "type": "integer",++ "minimum": 0,++ "description": "How many prompts the batch carries."++ },++ "items": {++ "type": "array",++ "items": {++ "$ref": "#/components/schemas/ai.batch-item.v1"++ },++ "description": "The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id."++ },++ "discount": {++ "type": "string",++ "description": "The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half)."++ },++ "deadline_at": {++ "type": "string",++ "format": "date-time",++ "description": "When the batch stops waiting: the deadline the caller asked for (1 to 72 hours)."++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "finished_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "absorbed_nano_usd": {++ "type": [++ "string",++ "null"++ ],++ "description": "What the network absorbed for this batch, in nano-USD, as a string."++ },++ "ran_on_regular_capacity": {++ "type": "boolean",++ "description": "The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount."++ }++ }++ },"ai.credential-put.v1": {"title": "AiCredentialPut","description": "ai.credential-put@1 (OpenVibe.AI; roadmap WS-O task 2): a person's own provider key, stored by the service that holds their consent (Live, for a streamer's AI viewers), PUT /api/v1/credentials/{subject} with capability ai.credential.manage. OpenVibe.AI encrypts the key at rest and never returns it; runs that name the credential call that provider with it, never a shared key, and are limited by `budget_usd_per_day`. `base_url` is any https endpoint (OpenAI-compatible or Anthropic); OpenVibe.AI connects only to public addresses. Provider `node` (contracts 0.220.0) is the person's own machine instead: `machine` names it, there is no key or base_url, and a run reaches it only through OpenVibe.Bot (capability bot.machine.ai), only for that person, where they turned on \"Let my AI use this computer's models\".",@@@ -6895,7 +7452,7 @@},"ai.run-request.v1": {"title": "AiRunRequest",-- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",++ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).","type": "object","required": ["workflow"@@@ -6944,6 +7501,20 @@"type": "boolean","default": false,"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."++ },++ "latency": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped)."++ },++ "deadline_hours": {++ "type": "integer",++ "minimum": 1,++ "maximum": 72,++ "description": "For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default."}}},@@@ -6965,7 +7536,7 @@},"ai.run.v1": {"title": "AiRun",-- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",++ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.","type": "object","required": ["id",@@@ -7263,6 +7834,59 @@"minimum": 0,"description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."},++ "latency_class": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows."++ },++ "service_tier": {++ "type": [++ "string",++ "null"++ ],++ "enum": [++ "standard",++ "flex",++ "batch",++ "offpeak",++ "local",++ null++ ],++ "description": "The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving."++ },++ "latency_clamped": {++ "type": [++ "object",++ "null"++ ],++ "required": [++ "asked",++ "used"++ ],++ "additionalProperties": false,++ "properties": {++ "asked": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the caller asked for."++ },++ "used": {++ "enum": [++ "interactive",++ "background",++ "batch"++ ],++ "description": "The class the run really ran in."++ }++ },++ "description": "Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it."++ },"retry_of": {"type": ["string",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bench API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.Blog API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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 Connect API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.Design API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Domains API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Food API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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": "ai","name": "OpenVibe.AI","origin": "https://ai.openvibe.services",-- "operations": 66,-- "capabilities": 15++ "operations": 70,++ "capabilities": 17},{"service": "bench",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.Mail API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.MediaHub API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRestream API",-- "version": "0.335.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Play API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Quest API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Report API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Today API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Vision API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Work API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Zone API",-- "version": "0.335.0",-- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",++ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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"@@@ -158449,7 +158449,7 @@ export interface AiAttributionQuota {/** [email protected] (owner: ai) *//**-- * Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.++ * Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).*/export interface AiRunRequest {/**@@@ -158480,6 +158480,14 @@ export interface AiRunRequest {* Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on.*/debug?: boolean;++ /**++ * The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped).++ */++ latency?: "interactive" | "background" | "batch";++ /**++ * For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default.++ */++ deadline_hours?: number;};/*** Run with the person's own provider key (ai.credential@1) that this service stored, instead of a shared route: only that provider is called (no fallback), the result is never cached or taken from the cache, the shared paid budget is not spent, and the credential's own daily budget applies (429 quota.exceeded). Chat operations only (text, JSON, vision).@@@ -158491,7 +158499,7 @@ export interface AiRunRequest {/** [email protected] (owner: ai) *//**-- * A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.++ * A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.*/export interface AiRun {id: string;@@@ -158574,6 +158582,27 @@ export interface AiRun {* Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later.*/citations_count: number;++ /**++ * The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows.++ */++ latency_class?: "interactive" | "background" | "batch";++ /**++ * The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving.++ */++ service_tier?: "standard" | "flex" | "batch" | "offpeak" | "local" | null;++ /**++ * Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it.++ */++ latency_clamped?: {++ /**++ * The class the caller asked for.++ */++ asked: "interactive" | "background" | "batch";++ /**++ * The class the run really ran in.++ */++ used: "interactive" | "background" | "batch";++ } | null;retry_of: string | null;idempotency_key: string | null;trace_id: string | null;@@@ -159864,6 +159893,170 @@ export type AiRunCreateResult =citations: AiRunCitation[];};++/** [email protected] (owner: ai) */++/**++ * ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).++ */++export interface AiBatch {++ id: string;++ /**++ * queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it).++ */++ status: "queued" | "running" | "succeeded" | "failed" | "expired" | "cancelled";++ /**++ * Who pays for the batch.++ */++ owner_type: "user" | "app" | "service";++ owner_id: string;++ /**++ * The developer app's project, when an app owns the batch.++ */++ project_id: string | null;++ /**++ * How many prompts the batch carries.++ */++ item_count: number;++ /**++ * The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id.++ */++ items: AiBatchItem[];++ /**++ * The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half).++ */++ discount: string;++ /**++ * When the batch stops waiting: the deadline the caller asked for (1 to 72 hours).++ */++ deadline_at: string;++ created_at: string;++ updated_at: string;++ finished_at?: string | null;++ /**++ * What the network absorbed for this batch, in nano-USD, as a string.++ */++ absorbed_nano_usd?: string | null;++ /**++ * The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount.++ */++ ran_on_regular_capacity?: boolean;++}++++/** [email protected] (owner: ai) */++/**++ * ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.++ */++export interface AiBatchItem {++ /**++ * The item's place in the batch.++ */++ idx: number;++ /**++ * queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled).++ */++ status: "queued" | "waiting" | "running" | "succeeded" | "failed" | "expired" | "cancelled";++ /**++ * The model the item asked for, or the batch's own.++ */++ model: string | null;++ /**++ * The run the item made, when it made one.++ */++ run_id: string | null;++ /**++ * The answer the provider gave, as text.++ */++ answer: string | null;++ error_code: string | null;++ error_detail: string | null;++ /**++ * Why the item ended without an answer (it expired, it was cancelled).++ */++ reason: string | null;++ /**++ * Where the answer came from: a community offer, the regular capacity, or a provider's own batch API.++ */++ capacity: string | null;++ route_provider: string | null;++ route_model: string | null;++ /**++ * What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float.++ */++ cost_nano_usd?: string | null;++ /**++ * What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string.++ */++ absorbed_nano_usd?: string | null;++ /**++ * What the customer was charged, in nano-USD, as a string: half the retail card.++ */++ charged_nano_usd?: string | null;++ deadline_at: string;++ created_at: string;++ updated_at: string;++}++++/** [email protected] (owner: ai) */++/**++ * ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).++ */++export interface AiBatchCreateRequest {++ /**++ * The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.++ *++ * @minItems 1++ */++ items: [++ (++ | string++ | {++ prompt: string;++ /**++ * The model this item wants (a model key, or provider/model).++ */++ model?: string;++ }++ ),++ ...(++ | string++ | {++ prompt: string;++ /**++ * The model this item wants (a model key, or provider/model).++ */++ model?: string;++ }++ )[]++ ];++ /**++ * The model the whole batch asks for, when no item names one.++ */++ model?: string;++ /**++ * How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged.++ */++ deadline_hours?: number;++}++++/** [email protected] (owner: ai) */++/**++ * ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).++ */++export interface AiBatchCreateResult {++ batch: AiBatch;++}++++/** [email protected] (owner: ai) */++/**++ * ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.++ */++export type AiBatchReadResult =++ | {++ batches: AiBatch[];++ }++ | {++ batch: AiBatch;++ };++/** [email protected] (owner: ai) *//*** ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "ai.batches.create",++ "version": "1.0",++ "owner": "ai",++ "status": "active",++ "visibility": "first-party",++ "description": "Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel). A batch is answered later, through a provider's own batch API when there is one, and is billed at half the retail price. A first-party service needs this grant to open a batch on the network's own account; a person or a developer app pays with its own account.",++ "inputSchema": "ai.batch-create-request@1",++ "outputSchema": "ai.batch-create-result@1",++ "permissions": [++ "ai:batch"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "ai-batch",++ "events": [],++ "implementedBy": [++ "POST /api/v1/batches",++ "POST /api/v1/batches/:id/cancel"++ ]++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "id": "ai.batches.read",++ "version": "1.0",++ "owner": "ai",++ "status": "active",++ "visibility": "first-party",++ "description": "Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id). Somebody else's batch is not found.",++ "inputSchema": "common.no-body@1",++ "outputSchema": "ai.batch-read-result@1",++ "permissions": [++ "ai:read"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "ai-read",++ "events": [],++ "implementedBy": [++ "GET /api/v1/batches",++ "GET /api/v1/batches/:id"++ ]++}@@@ -25,6 +25,8 @@"ai.model.read","ai.provider.manage","ai.quota.attribution.manage",++ "ai.batches.create",++ "ai.batches.read","ai.run.create","ai.run.read","ai.usage.read",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.335.0",++ "version": "0.336.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",nnew file mode 100644@@@ -0,0 +1,89 @@++'use strict';++// BA3 (plan track BA): the latency classes and the batches contract. The run's options carry the class++// the caller asks for and, for a batch, how long the answer may take; the run's answer carries the class++// it really ran in, the tier it was priced on, and the clamp when the ask was too fast. The batches++// contract is the queue of many prompts that are answered later.++const assert = require('assert');++const fs = require('fs');++const path = require('path');++const Ajv = require('ajv/dist/2020');++const addFormats = require('ajv-formats');++const c = require('..');++++const ROOT = path.join(__dirname, '..');++const load = (rel) => JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts', rel), 'utf8'));++++// ── The options: the class the caller asks for ────────────────────────────++const req = load('ai/run-request.v1.json');++assert.deepStrictEqual(req.properties.options.properties.latency.enum, ['interactive', 'background', 'batch'],++ 'the classes are interactive, background and batch');++for (const cls of ['interactive', 'background', 'batch']) {++ assert.ok(c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: cls } }).valid,++ 'a run may ask for the ' + cls + ' class');++}++assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: 'later' } }).valid,++ 'a class that does not exist is refused');++assert.ok(c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: 'batch', deadline_hours: 1 } }).valid,++ 'a batch run may ask for one hour');++assert.ok(c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: 'batch', deadline_hours: 72 } }).valid,++ 'a batch run may ask for seventy-two hours');++assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { deadline_hours: 0 } }).valid,++ 'no hour is not a deadline');++assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { deadline_hours: 73 } }).valid,++ 'more than seventy-two hours is not a deadline');++assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { deadline_hours: 1.5 } }).valid,++ 'a deadline is a whole number of hours');++++// ── The answer: the class it ran in, the tier it was priced on, the clamp ──++// The run's own document has many parts (a provenance, a requester, a usage), so the three new members are++// checked against the run's schema with the required list dropped: what matters here is that the members++// exist, that they hold the right values, and that a run that does not carry them is still a valid run.++const run = load('ai/run.v1.json');++for (const name of ['latency_class', 'service_tier', 'latency_clamped']) {++ assert.ok(run.properties[name], 'the run answers with ' + name);++ assert.ok(!run.required.includes(name), name + ' is optional: a run that does not carry it stays a valid run');++}++const ajv = new Ajv({ strict: false, allErrors: true });++addFormats(ajv);++const member = (name) => ajv.compile(run.properties[name]);++const latencyClass = member('latency_class');++const serviceTier = member('service_tier');++const latencyClamped = member('latency_clamped');++assert.ok(latencyClass('batch'), 'a run may answer with the batch class');++assert.ok(!latencyClass('now'), 'a class that does not exist is refused');++for (const tier of ['standard', 'flex', 'batch', 'offpeak', 'local']) {++ assert.ok(serviceTier(tier), 'the tier ' + tier + ' is a tier');++}++assert.ok(serviceTier(null), 'the tier is unknown until the run answers');++assert.ok(!serviceTier('cheapest'), 'a tier that does not exist is refused');++assert.ok(latencyClamped({ asked: 'interactive', used: 'background' }),++ 'a clamped run says which class was asked for and which was used');++assert.ok(!latencyClamped({ asked: 'interactive' }), 'a clamp without the class used is refused');++assert.ok(!latencyClamped({ asked: 'now', used: 'background' }), 'a clamp names a class that exists');++++// ── The batches: many prompts, answered later ──────────────────────────────++const batchDoc = {++ id: 'bat_0123456789abcdef01234567', status: 'queued', owner_type: 'user', owner_id: 'usr_1', project_id: null,++ item_count: 2, items: [], discount: '500000', deadline_at: '2026-11-08T22:58:00.000Z',++ created_at: '2026-11-07T22:58:00.000Z', updated_at: '2026-11-07T22:58:00.000Z',++};++assert.ok(c.validate('ai.batch@1', batchDoc).valid, 'a batch is a queue with a deadline');++assert.ok(c.validate('ai.batch-read-result@1', { batches: [batchDoc] }).valid, 'the list of batches answers with batches');++assert.ok(c.validate('ai.batch-read-result@1', { batch: batchDoc }).valid, 'one batch answers with a batch');++assert.ok(!c.validate('ai.batch-read-result@1', { batch: batchDoc, batches: [] }).valid, 'the read answers one way or the other');++assert.ok(c.validate('ai.batch-create-request@1', { items: ['one prompt', { prompt: 'another', model: 'gpt-4o-mini' }] }).valid,++ 'a batch carries prompts');++assert.ok(c.validate('ai.batch-create-request@1', { items: ['a prompt'], deadline_hours: 24 }).valid, 'a batch may ask for a day');++assert.ok(!c.validate('ai.batch-create-request@1', { items: ['a prompt'], deadline_hours: 100 }).valid,++ 'a batch deadline outside 1 to 72 hours is refused');++assert.ok(!c.validate('ai.batch-create-request@1', { items: [] }).valid, 'a batch without prompts is refused');++assert.ok(c.validate('ai.batch-create-result@1', { batch: batchDoc }).valid, 'opening a batch answers with the batch');++++// ── The catalog and the grants ─────────────────────────────────────────────++for (const id of ['ai.batch', 'ai.batch-item', 'ai.batch-create-request', 'ai.batch-create-result', 'ai.batch-read-result']) {++ assert.ok(c.catalog.some(e => e.id === id), id + ' is in the catalog');++}++assert.ok(c.capabilities.get('ai.batches.create'), 'the grant that opens a batch exists');++assert.ok(c.capabilities.get('ai.batches.read'), 'the grant that reads batches exists');++assert.ok(c.services.get('ai').capabilities.includes('ai.batches.create'), 'the AI service offers the grant that opens a batch');++assert.ok(c.services.get('ai').capabilities.includes('ai.batches.read'), 'the AI service offers the grant that reads batches');@@@ -960,6 +960,7 @@ sub(path.join(__dirname, 'billing-grace.test.js'));sub(path.join(__dirname, 'resources.test.js'));// The wait record (plan A2): a wait is a record, not a running loop.sub(path.join(__dirname, 'actor-wait.test.js'));++sub(path.join(__dirname, 'batching-latency.test.js'));sub(path.join(__dirname, 'network-consent.test.js'));// The machines hub (plan L34): one catalogue of roles, declared by the products that own them.sub(path.join(__dirname, 'machines.test.js'));
@@ -1,4 +1,22 @@
@@ -1,4 +1,22 @@
# Changelog
# Changelog
+## 0.336.0 — 2026-11-07
+
+- The run's options carry the latency class the caller asks for: `interactive` (answer as soon as the provider can),
+ `background` (minutes; the run is priced on the cheapest tier that can wait) or `batch` (hours; the run is queued and
+ answered through a provider's own batch API). A batch run may also say `deadline_hours` (1 to 72): how long the answer may
+ take. The class is the workflow's own (the route's `constraints.latency_class`): a caller may ask for a slower class and
+ never for a faster one, and a faster ask is not refused — the run happens in the workflow's own class and says so.
+- The run's answer carries `latency_class` (the class the run really ran in), `service_tier` (`standard`, `flex`,
+ `batch`, `offpeak`, `local`: the tier it was priced on — the one the provider served or the one the router chose) and
+ `latency_clamped` (the class asked for and the class used, when the ask was clamped).
+- Add the batches contract: `ai.batch@1`, `ai.batch-item@1`, `ai.batch-create-request@1`, `ai.batch-create-result@1` and
+ `ai.batch-read-result@1` for `/api/v1/batches` on OpenVibe.AI. A batch is a queue of many prompts that are answered
+ later, at half the retail price; the batch says where it stands, and a prompt that reaches its deadline without cheap
+ capacity expires and is never charged.
+- Add the grants `ai.batches.create` (open a batch, cancel one) and `ai.batches.read` (read the caller's own batches).
+ A first-party service opens a batch on the network's own account with `ai.batches.create`; a person or a developer app
+ pays with its own account.
+
## 0.335.0 — 2026-11-07
## 0.335.0 — 2026-11-07
new file mode 100644
new file mode 100644
@@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-create-request.v1.json",
+ "title": "AiBatchCreateRequest",
+ "description": "ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).",
+ "type": "object",
+ "required": [
+ "items"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "items": {
+ "type": "array",
+ "minItems": 1,
+ "description": "The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.",
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20000
+ },
+ {
+ "type": "object",
+ "required": [
+ "prompt"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "prompt": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20000
+ },
+ "model": {
+ "type": "string",
+ "description": "The model this item wants (a model key, or provider/model)."
+ }
+ }
+ }
+ ]
+ }
+ },
+ "model": {
+ "type": "string",
+ "description": "The model the whole batch asks for, when no item names one."
+ },
+ "deadline_hours": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 72,
+ "description": "How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged."
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-create-result.v1.json",
+ "title": "AiBatchCreateResult",
+ "description": "ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).",
+ "type": "object",
+ "required": [
+ "batch"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batch": {
+ "$ref": "batch.v1.json"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,136 @@
@@ -0,0 +1,136 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-item.v1.json",
+ "title": "AiBatchItem",
+ "description": "ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.",
+ "type": "object",
+ "required": [
+ "idx",
+ "status",
+ "model",
+ "run_id",
+ "answer",
+ "error_code",
+ "error_detail",
+ "reason",
+ "capacity",
+ "route_provider",
+ "route_model",
+ "deadline_at",
+ "created_at",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "idx": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The item's place in the batch."
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "waiting",
+ "running",
+ "succeeded",
+ "failed",
+ "expired",
+ "cancelled"
+ ],
+ "description": "queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled)."
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The model the item asked for, or the batch's own."
+ },
+ "run_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The run the item made, when it made one."
+ },
+ "answer": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The answer the provider gave, as text."
+ },
+ "error_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "error_detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Why the item ended without an answer (it expired, it was cancelled)."
+ },
+ "capacity": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Where the answer came from: a community offer, the regular capacity, or a provider's own batch API."
+ },
+ "route_provider": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "route_model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "cost_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float."
+ },
+ "absorbed_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string."
+ },
+ "charged_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the customer was charged, in nano-USD, as a string: half the retail card."
+ },
+ "deadline_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-read-result.v1.json",
+ "title": "AiBatchReadResult",
+ "description": "ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "batches"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batches": {
+ "type": "array",
+ "items": {
+ "$ref": "batch.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "batch"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batch": {
+ "$ref": "batch.v1.json"
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch.v1.json",
+ "title": "AiBatch",
+ "description": "ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).",
+ "type": "object",
+ "required": [
+ "id",
+ "status",
+ "owner_type",
+ "owner_id",
+ "project_id",
+ "item_count",
+ "items",
+ "discount",
+ "deadline_at",
+ "created_at",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^bat_[0-9a-f]{24}$"
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "running",
+ "succeeded",
+ "failed",
+ "expired",
+ "cancelled"
+ ],
+ "description": "queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it)."
+ },
+ "owner_type": {
+ "enum": [
+ "user",
+ "app",
+ "service"
+ ],
+ "description": "Who pays for the batch."
+ },
+ "owner_id": {
+ "type": "string"
+ },
+ "project_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The developer app's project, when an app owns the batch."
+ },
+ "item_count": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "How many prompts the batch carries."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "batch-item.v1.json"
+ },
+ "description": "The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id."
+ },
+ "discount": {
+ "type": "string",
+ "description": "The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half)."
+ },
+ "deadline_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the batch stops waiting: the deadline the caller asked for (1 to 72 hours)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "absorbed_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the network absorbed for this batch, in nano-USD, as a string."
+ },
+ "ran_on_regular_capacity": {
+ "type": "boolean",
+ "description": "The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount."
+ }
+ }
+}
@@ -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/ai/run-request.v1.json",
"$id": "https://openvibe.network/contracts/ai/run-request.v1.json",
"title": "AiRunRequest",
"title": "AiRunRequest",
- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",
+ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).",
"type": "object",
"type": "object",
"required": [
"required": [
"workflow"
"workflow"
@@ -51,6 +51,16 @@
@@ -51,6 +51,16 @@
"type": "boolean",
"type": "boolean",
"default": false,
"default": false,
"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
+ },
+ "latency": {
+ "enum": ["interactive", "background", "batch"],
+ "description": "The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped)."
+ },
+ "deadline_hours": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 72,
+ "description": "For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default."
}
}
}
}
},
},
@@ -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/ai/run.v1.json",
"$id": "https://openvibe.network/contracts/ai/run.v1.json",
"title": "AiRun",
"title": "AiRun",
- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",
+ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",
"type": "object",
"type": "object",
"required": ["id", "status", "workflow", "template", "route", "requester", "on_behalf_of", "attribution", "source_service", "target", "input", "output", "error", "synthetic", "provenance", "usage", "citations_count", "retry_of", "idempotency_key", "trace_id", "created_at", "started_at", "finished_at"],
"required": ["id", "status", "workflow", "template", "route", "requester", "on_behalf_of", "attribution", "source_service", "target", "input", "output", "error", "synthetic", "provenance", "usage", "citations_count", "retry_of", "idempotency_key", "trace_id", "created_at", "started_at", "finished_at"],
"additionalProperties": false,
"additionalProperties": false,
@@ -69,6 +69,25 @@
@@ -69,6 +69,25 @@
}
}
},
},
"citations_count": { "type": "integer", "minimum": 0, "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later." },
"citations_count": { "type": "integer", "minimum": 0, "description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later." },
+ "latency_class": {
+ "enum": ["interactive", "background", "batch"],
+ "description": "The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows."
+ },
+ "service_tier": {
+ "type": ["string", "null"],
+ "enum": ["standard", "flex", "batch", "offpeak", "local", null],
+ "description": "The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving."
+ },
+ "latency_clamped": {
+ "type": ["object", "null"],
+ "required": ["asked", "used"],
+ "additionalProperties": false,
+ "properties": {
+ "asked": { "enum": ["interactive", "background", "batch"], "description": "The class the caller asked for." },
+ "used": { "enum": ["interactive", "background", "batch"], "description": "The class the run really ran in." }
+ },
+ "description": "Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it."
+ },
"retry_of": { "type": ["string", "null"], "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$" },
"retry_of": { "type": ["string", "null"], "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$" },
"idempotency_key": { "type": ["string", "null"] },
"idempotency_key": { "type": ["string", "null"] },
"trace_id": { "type": ["string", "null"] },
"trace_id": { "type": ["string", "null"] },
@@ -1630,6 +1630,51 @@
@@ -1630,6 +1630,51 @@
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
},
},
+ {
+ "id": "ai.batch",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/batch.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.batch-item",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/batch-item.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.batch-create-request",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/batch-create-request.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.batch-create-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/batch-create-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "ai.batch-read-result",
+ "version": "1.0.0",
+ "owner": "ai",
+ "schema": "ai/batch-read-result.v1.json",
+ "visibility": "first-party",
+ "compatibility": "backward",
+ "status": "active"
+ },
{
{
"id": "ai.run-read-result",
"id": "ai.run-read-result",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
+{
+ "items": [
+ "one prompt"
+ ],
+ "deadline_hours": 100
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "items": [
+ ""
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "items": []
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
+{
+ "items": [
+ "one prompt",
+ {
+ "prompt": "another prompt",
+ "model": "gpt-4o-mini"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+{
+ "items": [
+ "one prompt"
+ ],
+ "model": "gpt-4o-mini",
+ "deadline_hours": 24
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "batch_id": "bat_0123456789abcdef01234567"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
+{
+ "batch": {
+ "id": "bat_0123456789abcdef01234567",
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "idx": 0,
+ "status": "later",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
+{
+ "batch": {
+ "id": "bat_0123456789abcdef01234567",
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+ },
+ "batches": [
+ {
+ "id": "bat_0123456789abcdef01234567",
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
+{
+ "batch": {
+ "id": "bat_0123456789abcdef01234567",
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
+{
+ "batches": [
+ {
+ "id": "bat_0123456789abcdef01234567",
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
+{
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
+{
+ "id": "bat_0123456789abcdef01234567",
+ "status": "whenever",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+{
+ "id": "bat_0123456789abcdef01234567",
+ "status": "queued",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 2,
+ "items": [],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": null,
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
+{
+ "id": "bat_0123456789abcdef01234567",
+ "status": "succeeded",
+ "owner_type": "user",
+ "owner_id": "usr_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "project_id": null,
+ "item_count": 1,
+ "items": [
+ {
+ "idx": 0,
+ "status": "succeeded",
+ "model": "gpt-4o-mini",
+ "run_id": "run_01K6R3A1B2C3D4E5F6G7H8J9KF",
+ "answer": "A digest of the week.",
+ "error_code": null,
+ "error_detail": null,
+ "reason": null,
+ "capacity": "regular",
+ "route_provider": "openai",
+ "route_model": "gpt-4o-mini",
+ "cost_nano_usd": "1200",
+ "absorbed_nano_usd": "0",
+ "charged_nano_usd": "600",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z"
+ }
+ ],
+ "discount": "500000",
+ "deadline_at": "2026-11-08T22:58:00.000Z",
+ "created_at": "2026-11-07T22:58:00.000Z",
+ "updated_at": "2026-11-08T00:10:00.000Z",
+ "finished_at": "2026-11-08T00:10:00.000Z",
+ "absorbed_nano_usd": "0",
+ "ran_on_regular_capacity": false
+}
@@ -15271,7 +15271,7 @@
@@ -15271,7 +15271,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/ai/run-request.v1.json",
"$id": "https://openvibe.network/contracts/ai/run-request.v1.json",
"title": "AiRunRequest",
"title": "AiRunRequest",
- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",
+ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).",
"type": "object",
"type": "object",
"required": [
"required": [
"workflow"
"workflow"
@@ -15320,6 +15320,20 @@
@@ -15320,6 +15320,20 @@
"type": "boolean",
"type": "boolean",
"default": false,
"default": false,
"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
+ },
+ "latency": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped)."
+ },
+ "deadline_hours": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 72,
+ "description": "For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default."
}
}
}
}
},
},
@@ -15348,7 +15362,7 @@
@@ -15348,7 +15362,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/ai/run.v1.json",
"$id": "https://openvibe.network/contracts/ai/run.v1.json",
"title": "AiRun",
"title": "AiRun",
- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",
+ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",
"type": "object",
"type": "object",
"required": [
"required": [
"id",
"id",
@@ -15646,6 +15660,59 @@
@@ -15646,6 +15660,59 @@
"minimum": 0,
"minimum": 0,
"description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."
"description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."
},
},
+ "latency_class": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows."
+ },
+ "service_tier": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "standard",
+ "flex",
+ "batch",
+ "offpeak",
+ "local",
+ null
+ ],
+ "description": "The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving."
+ },
+ "latency_clamped": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "asked",
+ "used"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "asked": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the caller asked for."
+ },
+ "used": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the run really ran in."
+ }
+ },
+ "description": "Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it."
+ },
"retry_of": {
"retry_of": {
"type": [
"type": [
"string",
"string",
@@ -18835,6 +18902,376 @@
@@ -18835,6 +18902,376 @@
]
]
}
}
},
},
+ "ai.batch": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch.v1.json",
+ "title": "AiBatch",
+ "description": "ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).",
+ "type": "object",
+ "required": [
+ "id",
+ "status",
+ "owner_type",
+ "owner_id",
+ "project_id",
+ "item_count",
+ "items",
+ "discount",
+ "deadline_at",
+ "created_at",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^bat_[0-9a-f]{24}$"
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "running",
+ "succeeded",
+ "failed",
+ "expired",
+ "cancelled"
+ ],
+ "description": "queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it)."
+ },
+ "owner_type": {
+ "enum": [
+ "user",
+ "app",
+ "service"
+ ],
+ "description": "Who pays for the batch."
+ },
+ "owner_id": {
+ "type": "string"
+ },
+ "project_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The developer app's project, when an app owns the batch."
+ },
+ "item_count": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "How many prompts the batch carries."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "batch-item.v1.json"
+ },
+ "description": "The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id."
+ },
+ "discount": {
+ "type": "string",
+ "description": "The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half)."
+ },
+ "deadline_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the batch stops waiting: the deadline the caller asked for (1 to 72 hours)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "absorbed_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the network absorbed for this batch, in nano-USD, as a string."
+ },
+ "ran_on_regular_capacity": {
+ "type": "boolean",
+ "description": "The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount."
+ }
+ }
+ }
+ },
+ "ai.batch-item": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-item.v1.json",
+ "title": "AiBatchItem",
+ "description": "ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.",
+ "type": "object",
+ "required": [
+ "idx",
+ "status",
+ "model",
+ "run_id",
+ "answer",
+ "error_code",
+ "error_detail",
+ "reason",
+ "capacity",
+ "route_provider",
+ "route_model",
+ "deadline_at",
+ "created_at",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "idx": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The item's place in the batch."
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "waiting",
+ "running",
+ "succeeded",
+ "failed",
+ "expired",
+ "cancelled"
+ ],
+ "description": "queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled)."
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The model the item asked for, or the batch's own."
+ },
+ "run_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The run the item made, when it made one."
+ },
+ "answer": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The answer the provider gave, as text."
+ },
+ "error_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "error_detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Why the item ended without an answer (it expired, it was cancelled)."
+ },
+ "capacity": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Where the answer came from: a community offer, the regular capacity, or a provider's own batch API."
+ },
+ "route_provider": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "route_model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "cost_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float."
+ },
+ "absorbed_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string."
+ },
+ "charged_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the customer was charged, in nano-USD, as a string: half the retail card."
+ },
+ "deadline_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "ai.batch-create-request": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-create-request.v1.json",
+ "title": "AiBatchCreateRequest",
+ "description": "ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).",
+ "type": "object",
+ "required": [
+ "items"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "items": {
+ "type": "array",
+ "minItems": 1,
+ "description": "The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.",
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20000
+ },
+ {
+ "type": "object",
+ "required": [
+ "prompt"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "prompt": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20000
+ },
+ "model": {
+ "type": "string",
+ "description": "The model this item wants (a model key, or provider/model)."
+ }
+ }
+ }
+ ]
+ }
+ },
+ "model": {
+ "type": "string",
+ "description": "The model the whole batch asks for, when no item names one."
+ },
+ "deadline_hours": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 72,
+ "description": "How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged."
+ }
+ }
+ }
+ },
+ "ai.batch-create-result": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-create-result.v1.json",
+ "title": "AiBatchCreateResult",
+ "description": "ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).",
+ "type": "object",
+ "required": [
+ "batch"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batch": {
+ "$ref": "batch.v1.json"
+ }
+ }
+ }
+ },
+ "ai.batch-read-result": {
+ "version": "1.0.0",
+ "owner": "ai",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/ai/batch-read-result.v1.json",
+ "title": "AiBatchReadResult",
+ "description": "ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "batches"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batches": {
+ "type": "array",
+ "items": {
+ "$ref": "batch.v1.json"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "batch"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batch": {
+ "$ref": "batch.v1.json"
+ }
+ }
+ }
+ ]
+ }
+ },
"ai.run-read-result": {
"ai.run-read-result": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "ai",
"owner": "ai",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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"
@@ -288,6 +288,228 @@
@@ -288,6 +288,228 @@
}
}
}
}
},
},
+ "/api/v1/batches": {
+ "get": {
+ "operationId": "get_api_v1_batches",
+ "summary": "Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id).",
+ "description": "**ai.batches.read** (first-party): Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id). Somebody else's batch is not found.",
+ "tags": [
+ "ai.batches"
+ ],
+ "x-openvibe-capabilities": [
+ "ai.batches.read"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "ai-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "ai.batches.read"
+ ]
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ai.batch-read-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_batches",
+ "summary": "Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel).",
+ "description": "**ai.batches.create** (first-party): Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel). A batch is answered later, through a provider's own batch API when there is one, and is billed at half the retail price. A first-party service needs this grant to open a batch on the network's own account; a person or a developer app pays with its own account.",
+ "tags": [
+ "ai.batches"
+ ],
+ "x-openvibe-capabilities": [
+ "ai.batches.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "ai-batch",
+ "security": [
+ {
+ "openvibeToken": [
+ "ai.batches.create"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ai.batch-create-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ai.batch-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/batches/{id}": {
+ "get": {
+ "operationId": "get_api_v1_batches_id",
+ "summary": "Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id).",
+ "description": "**ai.batches.read** (first-party): Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id). Somebody else's batch is not found.",
+ "tags": [
+ "ai.batches"
+ ],
+ "x-openvibe-capabilities": [
+ "ai.batches.read"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "ai-read",
+ "security": [
+ {
+ "openvibeToken": [
+ "ai.batches.read"
+ ]
+ }
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ai.batch-read-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/batches/{id}/cancel": {
+ "post": {
+ "operationId": "post_api_v1_batches_id_cancel",
+ "summary": "Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel).",
+ "description": "**ai.batches.create** (first-party): Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel). A batch is answered later, through a provider's own batch API when there is one, and is billed at half the retail price. A first-party service needs this grant to open a batch on the network's own account; a person or a developer app pays with its own account.",
+ "tags": [
+ "ai.batches"
+ ],
+ "x-openvibe-capabilities": [
+ "ai.batches.create"
+ ],
+ "x-openvibe-visibility": [
+ "first-party"
+ ],
+ "x-openvibe-quota-class": "ai-batch",
+ "security": [
+ {
+ "openvibeToken": [
+ "ai.batches.create"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ai.batch-create-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ai.batch-create-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
"/api/v1/cache": {
"/api/v1/cache": {
"delete": {
"delete": {
"operationId": "delete_api_v1_cache",
"operationId": "delete_api_v1_cache",
@@ -4726,6 +4948,341 @@
@@ -4726,6 +4948,341 @@
}
}
}
}
},
},
+ "ai.batch-create-request.v1": {
+ "title": "AiBatchCreateRequest",
+ "description": "ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).",
+ "type": "object",
+ "required": [
+ "items"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "items": {
+ "type": "array",
+ "minItems": 1,
+ "description": "The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.",
+ "items": {
+ "anyOf": [
+ {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20000
+ },
+ {
+ "type": "object",
+ "required": [
+ "prompt"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "prompt": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 20000
+ },
+ "model": {
+ "type": "string",
+ "description": "The model this item wants (a model key, or provider/model)."
+ }
+ }
+ }
+ ]
+ }
+ },
+ "model": {
+ "type": "string",
+ "description": "The model the whole batch asks for, when no item names one."
+ },
+ "deadline_hours": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 72,
+ "description": "How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged."
+ }
+ }
+ },
+ "ai.batch-create-result.v1": {
+ "title": "AiBatchCreateResult",
+ "description": "ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).",
+ "type": "object",
+ "required": [
+ "batch"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batch": {
+ "$ref": "#/components/schemas/ai.batch.v1"
+ }
+ }
+ },
+ "ai.batch-item.v1": {
+ "title": "AiBatchItem",
+ "description": "ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.",
+ "type": "object",
+ "required": [
+ "idx",
+ "status",
+ "model",
+ "run_id",
+ "answer",
+ "error_code",
+ "error_detail",
+ "reason",
+ "capacity",
+ "route_provider",
+ "route_model",
+ "deadline_at",
+ "created_at",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "idx": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The item's place in the batch."
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "waiting",
+ "running",
+ "succeeded",
+ "failed",
+ "expired",
+ "cancelled"
+ ],
+ "description": "queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled)."
+ },
+ "model": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The model the item asked for, or the batch's own."
+ },
+ "run_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "pattern": "^run_[0-9A-HJKMNP-TV-Z]{26}$",
+ "description": "The run the item made, when it made one."
+ },
+ "answer": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The answer the provider gave, as text."
+ },
+ "error_code": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "error_detail": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Why the item ended without an answer (it expired, it was cancelled)."
+ },
+ "capacity": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "Where the answer came from: a community offer, the regular capacity, or a provider's own batch API."
+ },
+ "route_provider": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "route_model": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "cost_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float."
+ },
+ "absorbed_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string."
+ },
+ "charged_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the customer was charged, in nano-USD, as a string: half the retail card."
+ },
+ "deadline_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "ai.batch-read-result.v1": {
+ "title": "AiBatchReadResult",
+ "description": "ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.",
+ "anyOf": [
+ {
+ "type": "object",
+ "required": [
+ "batches"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batches": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ai.batch.v1"
+ }
+ }
+ }
+ },
+ {
+ "type": "object",
+ "required": [
+ "batch"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "batch": {
+ "$ref": "#/components/schemas/ai.batch.v1"
+ }
+ }
+ }
+ ]
+ },
+ "ai.batch.v1": {
+ "title": "AiBatch",
+ "description": "ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).",
+ "type": "object",
+ "required": [
+ "id",
+ "status",
+ "owner_type",
+ "owner_id",
+ "project_id",
+ "item_count",
+ "items",
+ "discount",
+ "deadline_at",
+ "created_at",
+ "updated_at"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^bat_[0-9a-f]{24}$"
+ },
+ "status": {
+ "enum": [
+ "queued",
+ "running",
+ "succeeded",
+ "failed",
+ "expired",
+ "cancelled"
+ ],
+ "description": "queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it)."
+ },
+ "owner_type": {
+ "enum": [
+ "user",
+ "app",
+ "service"
+ ],
+ "description": "Who pays for the batch."
+ },
+ "owner_id": {
+ "type": "string"
+ },
+ "project_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "The developer app's project, when an app owns the batch."
+ },
+ "item_count": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "How many prompts the batch carries."
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ai.batch-item.v1"
+ },
+ "description": "The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id."
+ },
+ "discount": {
+ "type": "string",
+ "description": "The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half)."
+ },
+ "deadline_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the batch stops waiting: the deadline the caller asked for (1 to 72 hours)."
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "finished_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "absorbed_nano_usd": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "description": "What the network absorbed for this batch, in nano-USD, as a string."
+ },
+ "ran_on_regular_capacity": {
+ "type": "boolean",
+ "description": "The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount."
+ }
+ }
+ },
"ai.credential-put.v1": {
"ai.credential-put.v1": {
"title": "AiCredentialPut",
"title": "AiCredentialPut",
"description": "ai.credential-put@1 (OpenVibe.AI; roadmap WS-O task 2): a person's own provider key, stored by the service that holds their consent (Live, for a streamer's AI viewers), PUT /api/v1/credentials/{subject} with capability ai.credential.manage. OpenVibe.AI encrypts the key at rest and never returns it; runs that name the credential call that provider with it, never a shared key, and are limited by `budget_usd_per_day`. `base_url` is any https endpoint (OpenAI-compatible or Anthropic); OpenVibe.AI connects only to public addresses. Provider `node` (contracts 0.220.0) is the person's own machine instead: `machine` names it, there is no key or base_url, and a run reaches it only through OpenVibe.Bot (capability bot.machine.ai), only for that person, where they turned on \"Let my AI use this computer's models\".",
"description": "ai.credential-put@1 (OpenVibe.AI; roadmap WS-O task 2): a person's own provider key, stored by the service that holds their consent (Live, for a streamer's AI viewers), PUT /api/v1/credentials/{subject} with capability ai.credential.manage. OpenVibe.AI encrypts the key at rest and never returns it; runs that name the credential call that provider with it, never a shared key, and are limited by `budget_usd_per_day`. `base_url` is any https endpoint (OpenAI-compatible or Anthropic); OpenVibe.AI connects only to public addresses. Provider `node` (contracts 0.220.0) is the person's own machine instead: `machine` names it, there is no key or base_url, and a run reaches it only through OpenVibe.Bot (capability bot.machine.ai), only for that person, where they turned on \"Let my AI use this computer's models\".",
@@ -6895,7 +7452,7 @@
@@ -6895,7 +7452,7 @@
},
},
"ai.run-request.v1": {
"ai.run-request.v1": {
"title": "AiRunRequest",
"title": "AiRunRequest",
- "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.",
+ "description": "Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).",
"type": "object",
"type": "object",
"required": [
"required": [
"workflow"
"workflow"
@@ -6944,6 +7501,20 @@
@@ -6944,6 +7501,20 @@
"type": "boolean",
"type": "boolean",
"default": false,
"default": false,
"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
"description": "Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on."
+ },
+ "latency": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped)."
+ },
+ "deadline_hours": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 72,
+ "description": "For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default."
}
}
}
}
},
},
@@ -6965,7 +7536,7 @@
@@ -6965,7 +7536,7 @@
},
},
"ai.run.v1": {
"ai.run.v1": {
"title": "AiRun",
"title": "AiRun",
- "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",
+ "description": "A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.",
"type": "object",
"type": "object",
"required": [
"required": [
"id",
"id",
@@ -7263,6 +7834,59 @@
@@ -7263,6 +7834,59 @@
"minimum": 0,
"minimum": 0,
"description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."
"description": "Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later."
},
},
+ "latency_class": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows."
+ },
+ "service_tier": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "standard",
+ "flex",
+ "batch",
+ "offpeak",
+ "local",
+ null
+ ],
+ "description": "The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving."
+ },
+ "latency_clamped": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "required": [
+ "asked",
+ "used"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "asked": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the caller asked for."
+ },
+ "used": {
+ "enum": [
+ "interactive",
+ "background",
+ "batch"
+ ],
+ "description": "The class the run really ran in."
+ }
+ },
+ "description": "Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it."
+ },
"retry_of": {
"retry_of": {
"type": [
"type": [
"string",
"string",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bench API",
"title": "OpenVibe.Bench API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Bench as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Codes as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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 Connect API",
"title": "OpenVibe Connect API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe Connect as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.Design API",
"title": "OpenVibe.Design API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Design as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Domains API",
"title": "OpenVibe.Domains API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Domains as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Food API",
"title": "OpenVibe.Food API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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": "ai",
"service": "ai",
"name": "OpenVibe.AI",
"name": "OpenVibe.AI",
"origin": "https://ai.openvibe.services",
"origin": "https://ai.openvibe.services",
- "operations": 66,
- "capabilities": 15
+ "operations": 70,
+ "capabilities": 17
},
},
{
{
"service": "bench",
"service": "bench",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.Mail API",
"title": "OpenVibe.Mail API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Mail as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.MediaHub API",
"title": "OpenVibe.MediaHub API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.335.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Play API",
"title": "OpenVibe.Play API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Play as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Quest API",
"title": "OpenVibe.Quest API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Quest as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Report API",
"title": "OpenVibe.Report API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Report as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Today API",
"title": "OpenVibe.Today API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Today as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Vision API",
"title": "OpenVibe.Vision API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Vision as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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.335.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Work API",
"title": "OpenVibe.Work API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Zone API",
"title": "OpenVibe.Zone API",
- "version": "0.335.0",
- "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.335.0): each operation lists the capabilities it performs; a token for 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.336.0",
+ "description": "Routes of OpenVibe.Zone as the OpenVibe contracts describe them (openvibe-contracts 0.336.0): each operation lists the capabilities it performs; a token for 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"
@@ -158449,7 +158449,7 @@ export interface AiAttributionQuota {
@@ -158449,7 +158449,7 @@ export interface AiAttributionQuota {
/** [email protected] (owner: ai) */
/** [email protected] (owner: ai) */
/**
/**
- * Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas.
+ * Body of POST /api/v1/runs on OpenVibe.AI (capability ai.run.create; ADR-015). Runs the newest active version of a workflow (or the given active or deprecated version) on input that must match that workflow version's own input schema; per-workflow input and output schemas live in AI's registry (GET /api/v1/workflows/:key), not here. The token's ns claim limits which workflow namespaces may run (403 capability.namespace_denied). ?wait=ms waits for the result: 201 finished or served from cache, 202 still queued/running (poll GET /api/v1/runs/:id), 200 an idempotent replay. Errors are problem+json: 404 workflow.not_found, 409 workflow.inactive, 409 idempotency.conflict, 413 input.too_large, 422 input.invalid, 429 quota.exceeded or queue.full with Retry-After. The direct operations POST /api/v1/{chat,generate,summarize,classify,extract,enrich,embed} take the same fields (except workflow and version) with the input fields at the top level and run workflow ai.<op>. target (EntityRef): what the output is about, part of the cache scope and a run filter. attribution (EntityRef): what the spend is attributed to for quotas and usage. on_behalf_of (SubjectRef): the person or actor the caller acts for, part of the cache scope and per-actor quotas. options.latency asks for a slower class than the workflow allows (options.deadline_hours says how long a batch run may take); the run answers with the class it ran in (run.latency_class), the tier it was priced on (run.service_tier) and, when the ask was clamped, the class it really ran in (run.latency_clamped).
*/
*/
export interface AiRunRequest {
export interface AiRunRequest {
/**
/**
@@ -158480,6 +158480,14 @@ export interface AiRunRequest {
@@ -158480,6 +158480,14 @@ export interface AiRunRequest {
* Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on.
* Keep raw prompts and responses in the request log, only when AI runs with raw debug logging on.
*/
*/
debug?: boolean;
debug?: boolean;
+ /**
+ * The class the caller asks for: how long the caller may wait, and what the router may do with the wait. interactive (the default) answers as soon as the provider can; background may take minutes, and the run is priced on the cheapest tier that can wait (a Flex tier, a provider's off-peak hours, the host's own idle model); batch may take hours, and the run is queued and answered through a provider's own batch API. The class is the workflow's own (the route's constraints.latency_class): a caller may ask for a SLOWER class and never for a faster one. A faster ask is not refused: the run happens in the workflow's own class and the answer says so (run.latency_clamped).
+ */
+ latency?: "interactive" | "background" | "batch";
+ /**
+ * For a batch run: how long the answer may take, in hours (1 to 72). The run is queued and answered within the deadline; a deadline the provider cannot meet sends the run back to the ordinary path. Omitted: the service's own default.
+ */
+ deadline_hours?: number;
};
};
/**
/**
* Run with the person's own provider key (ai.credential@1) that this service stored, instead of a shared route: only that provider is called (no fallback), the result is never cached or taken from the cache, the shared paid budget is not spent, and the credential's own daily budget applies (429 quota.exceeded). Chat operations only (text, JSON, vision).
* Run with the person's own provider key (ai.credential@1) that this service stored, instead of a shared route: only that provider is called (no fallback), the result is never cached or taken from the cache, the shared paid budget is not spent, and the credential's own daily budget applies (429 quota.exceeded). Chat operations only (text, JSON, vision).
@@ -158491,7 +158499,7 @@ export interface AiRunRequest {
@@ -158491,7 +158499,7 @@ export interface AiRunRequest {
/** [email protected] (owner: ai) */
/** [email protected] (owner: ai) */
/**
/**
- * A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.
+ * A workflow run on OpenVibe.AI (ADR-015; server/runs.js decode): the `run` member of every run response (POST /api/v1/runs and the direct operations, GET /api/v1/runs/:id, cancel, retry) and each item of GET /api/v1/runs { runs }. Output is attributable to workflow + version + template version + route version + provider/model + run id, never to a person. A draft stays a draft: products store it with ai/ai_assisted authorship and publish nothing until a person reviews it. input keeps no inline media (replaced by data_url_sha256 and data_url_bytes) and no passthrough prompt ({ not_retained }); output is null until the run succeeded or was served from cache and then matches the workflow version's output_schema. requester is the token principal (service, app or mod). A run interrupted by a restart or shutdown ends failed with error.code run.interrupted. latency_class is the class the run really ran in, service_tier the tier it was priced on, and latency_clamped set when the caller asked for a faster class than the workflow allows. explain says why the run answered as it did: the task it read, the candidates it ranked and every attempt.
*/
*/
export interface AiRun {
export interface AiRun {
id: string;
id: string;
@@ -158574,6 +158582,27 @@ export interface AiRun {
@@ -158574,6 +158582,27 @@ export interface AiRun {
* Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later.
* Citations stored for the run (GET /api/v1/runs/:id/citations): the sources the workflow was given, and any the requester attached later.
*/
*/
citations_count: number;
citations_count: number;
+ /**
+ * The class the run really ran in: how long the caller was willing to wait. It is the workflow's own class (the route's constraints.latency_class) or a slower one the caller asked for (options.latency); a caller can never make a run faster than its workflow allows.
+ */
+ latency_class?: "interactive" | "background" | "batch";
+ /**
+ * The tier the run was priced on: the tier the provider really served (standard, flex, batch) or the one the router chose (offpeak inside a provider's off-peak hours, local on the host's own model). Null while the run has not answered. A provider that does not say which tier it served is priced at the list price: a saving nobody confirmed is not a saving.
+ */
+ service_tier?: "standard" | "flex" | "batch" | "offpeak" | "local" | null;
+ /**
+ * Set when the caller asked for a faster class than the workflow allows: the class asked for and the class the run really ran in. The run is never refused for it.
+ */
+ latency_clamped?: {
+ /**
+ * The class the caller asked for.
+ */
+ asked: "interactive" | "background" | "batch";
+ /**
+ * The class the run really ran in.
+ */
+ used: "interactive" | "background" | "batch";
+ } | null;
retry_of: string | null;
retry_of: string | null;
idempotency_key: string | null;
idempotency_key: string | null;
trace_id: string | null;
trace_id: string | null;
@@ -159864,6 +159893,170 @@ export type AiRunCreateResult =
@@ -159864,6 +159893,170 @@ export type AiRunCreateResult =
citations: AiRunCitation[];
citations: AiRunCitation[];
};
};
+/** [email protected] (owner: ai) */
+/**
+ * ai.batch@1: a batch of many prompts on OpenVibe.AI (POST /api/v1/batches, GET /api/v1/batches, GET /api/v1/batches/:id). The answers are not waited for: the batch is queued, and the batch says where it stands. A batch is billed at half the retail price through Billing like every other request; the batch table is the queue and the status, never a second ledger. The owner of the batch is the account that pays for it: a person, a developer app's project, or a first-party service with the ai.batches.create grant (the network's own account).
+ */
+export interface AiBatch {
+ id: string;
+ /**
+ * queued (asked for), running (items are being answered), succeeded, failed (an item failed), expired (the deadline passed with nothing answered), cancelled (the caller cancelled it).
+ */
+ status: "queued" | "running" | "succeeded" | "failed" | "expired" | "cancelled";
+ /**
+ * Who pays for the batch.
+ */
+ owner_type: "user" | "app" | "service";
+ owner_id: string;
+ /**
+ * The developer app's project, when an app owns the batch.
+ */
+ project_id: string | null;
+ /**
+ * How many prompts the batch carries.
+ */
+ item_count: number;
+ /**
+ * The items, in order. The list of batches answers with the same objects and no items (an empty array): the items come from GET /api/v1/batches/:id.
+ */
+ items: AiBatchItem[];
+ /**
+ * The share of the retail card the customer is charged, as a decimal fraction of a million (500000 = half).
+ */
+ discount: string;
+ /**
+ * When the batch stops waiting: the deadline the caller asked for (1 to 72 hours).
+ */
+ deadline_at: string;
+ created_at: string;
+ updated_at: string;
+ finished_at?: string | null;
+ /**
+ * What the network absorbed for this batch, in nano-USD, as a string.
+ */
+ absorbed_nano_usd?: string | null;
+ /**
+ * The batch ran on the ordinary capacity (not on free or community capacity): it cost the network more than the customer paid, and the difference is the absorbed amount.
+ */
+ ran_on_regular_capacity?: boolean;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.batch-item@1: one prompt of a batch on OpenVibe.AI (POST /api/v1/batches). The item is the queue and the status of that one prompt: it waits for capacity (or for a provider's own batch API), runs, and carries the answer, the run it made and the money. A prompt that reached its deadline without capacity expires and is never charged.
+ */
+export interface AiBatchItem {
+ /**
+ * The item's place in the batch.
+ */
+ idx: number;
+ /**
+ * queued (asked for, not started), waiting (no cheap capacity yet), running (a provider works on it), succeeded, failed, expired (the deadline passed; not charged), cancelled (the batch was cancelled).
+ */
+ status: "queued" | "waiting" | "running" | "succeeded" | "failed" | "expired" | "cancelled";
+ /**
+ * The model the item asked for, or the batch's own.
+ */
+ model: string | null;
+ /**
+ * The run the item made, when it made one.
+ */
+ run_id: string | null;
+ /**
+ * The answer the provider gave, as text.
+ */
+ answer: string | null;
+ error_code: string | null;
+ error_detail: string | null;
+ /**
+ * Why the item ended without an answer (it expired, it was cancelled).
+ */
+ reason: string | null;
+ /**
+ * Where the answer came from: a community offer, the regular capacity, or a provider's own batch API.
+ */
+ capacity: string | null;
+ route_provider: string | null;
+ route_model: string | null;
+ /**
+ * What the answer really cost, in nano-USD (1 USD = 1e9 nano), as a string: the money is never a float.
+ */
+ cost_nano_usd?: string | null;
+ /**
+ * What the network absorbed (the route cost more than the customer paid), in nano-USD, as a string.
+ */
+ absorbed_nano_usd?: string | null;
+ /**
+ * What the customer was charged, in nano-USD, as a string: half the retail card.
+ */
+ charged_nano_usd?: string | null;
+ deadline_at: string;
+ created_at: string;
+ updated_at: string;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.batch-create-request@1: the body of POST /api/v1/batches on OpenVibe.AI (capability ai.batches.create). One batch of many prompts: the answers come later, and the batch is billed at half the retail price. Errors are problem+json: 422 input.invalid (no items, an empty prompt, a prompt over 20000 characters, a deadline outside 1 to 72 hours), 422 batch.too_large (more prompts than the service's cap), 403 batch.no_payer (the token names no account to pay), 403 batch.no_grant (a service without the ai.batches.create grant).
+ */
+export interface AiBatchCreateRequest {
+ /**
+ * The prompts of the batch, in order. Each item is a prompt, or an object with a prompt and the model it wants.
+ *
+ * @minItems 1
+ */
+ items: [
+ (
+ | string
+ | {
+ prompt: string;
+ /**
+ * The model this item wants (a model key, or provider/model).
+ */
+ model?: string;
+ }
+ ),
+ ...(
+ | string
+ | {
+ prompt: string;
+ /**
+ * The model this item wants (a model key, or provider/model).
+ */
+ model?: string;
+ }
+ )[]
+ ];
+ /**
+ * The model the whole batch asks for, when no item names one.
+ */
+ model?: string;
+ /**
+ * How long the batch may take, in hours (1 to 72). Omitted: the service's own default (24 hours). A prompt that reaches the deadline without cheap capacity expires and is never charged.
+ */
+ deadline_hours?: number;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.batch-create-result@1: the answers of ai.batches.create on OpenVibe.AI. POST /api/v1/batches → 201 { batch }; POST /api/v1/batches/:id/cancel → { batch } (the batch is cancelled: the items that had not run are cancelled and nothing is charged for them).
+ */
+export interface AiBatchCreateResult {
+ batch: AiBatch;
+}
+
+/** [email protected] (owner: ai) */
+/**
+ * ai.batch-read-result@1: the answers of ai.batches.read on OpenVibe.AI (the caller's own batches). GET /api/v1/batches → { batches } (newest first, the items not included); GET /api/v1/batches/:id → { batch } (with its items). Somebody else's batch is 404 batch.not_found.
+ */
+export type AiBatchReadResult =
+ | {
+ batches: AiBatch[];
+ }
+ | {
+ batch: AiBatch;
+ };
+
/** [email protected] (owner: ai) */
/** [email protected] (owner: ai) */
/**
/**
* ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.
* ai.run-read-result@1: answers of ai.run.read on OpenVibe.AI (the caller's own runs; usage readers may ask for all). GET /api/v1/runs?status=&workflow=&target=&limit=&before= → { runs }; GET /api/v1/runs/:id → { run, citations, requests } (the request log is metadata only: hashes, tokens, cost, latency, never prompts); GET /api/v1/runs/:id/citations → { citations }. Someone else's run is 404 run.not_found.
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "ai.batches.create",
+ "version": "1.0",
+ "owner": "ai",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Open a batch of many prompts on OpenVibe.AI (POST /api/v1/batches) and cancel one (POST /api/v1/batches/:id/cancel). A batch is answered later, through a provider's own batch API when there is one, and is billed at half the retail price. A first-party service needs this grant to open a batch on the network's own account; a person or a developer app pays with its own account.",
+ "inputSchema": "ai.batch-create-request@1",
+ "outputSchema": "ai.batch-create-result@1",
+ "permissions": [
+ "ai:batch"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "ai-batch",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/batches",
+ "POST /api/v1/batches/:id/cancel"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "id": "ai.batches.read",
+ "version": "1.0",
+ "owner": "ai",
+ "status": "active",
+ "visibility": "first-party",
+ "description": "Read the caller's own batches and their items (GET /api/v1/batches, GET /api/v1/batches/:id). Somebody else's batch is not found.",
+ "inputSchema": "common.no-body@1",
+ "outputSchema": "ai.batch-read-result@1",
+ "permissions": [
+ "ai:read"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "ai-read",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/batches",
+ "GET /api/v1/batches/:id"
+ ]
+}
@@ -25,6 +25,8 @@
@@ -25,6 +25,8 @@
"ai.model.read",
"ai.model.read",
"ai.provider.manage",
"ai.provider.manage",
"ai.quota.attribution.manage",
"ai.quota.attribution.manage",
+ "ai.batches.create",
+ "ai.batches.read",
"ai.run.create",
"ai.run.create",
"ai.run.read",
"ai.run.read",
"ai.usage.read",
"ai.usage.read",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.335.0",
+ "version": "0.336.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",
new file mode 100644
new file mode 100644
@@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
+'use strict';
+// BA3 (plan track BA): the latency classes and the batches contract. The run's options carry the class
+// the caller asks for and, for a batch, how long the answer may take; the run's answer carries the class
+// it really ran in, the tier it was priced on, and the clamp when the ask was too fast. The batches
+// contract is the queue of many prompts that are answered later.
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const Ajv = require('ajv/dist/2020');
+const addFormats = require('ajv-formats');
+const c = require('..');
+
+const ROOT = path.join(__dirname, '..');
+const load = (rel) => JSON.parse(fs.readFileSync(path.join(ROOT, 'contracts', rel), 'utf8'));
+
+// ── The options: the class the caller asks for ────────────────────────────
+const req = load('ai/run-request.v1.json');
+assert.deepStrictEqual(req.properties.options.properties.latency.enum, ['interactive', 'background', 'batch'],
+ 'the classes are interactive, background and batch');
+for (const cls of ['interactive', 'background', 'batch']) {
+ assert.ok(c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: cls } }).valid,
+ 'a run may ask for the ' + cls + ' class');
+}
+assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: 'later' } }).valid,
+ 'a class that does not exist is refused');
+assert.ok(c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: 'batch', deadline_hours: 1 } }).valid,
+ 'a batch run may ask for one hour');
+assert.ok(c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { latency: 'batch', deadline_hours: 72 } }).valid,
+ 'a batch run may ask for seventy-two hours');
+assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { deadline_hours: 0 } }).valid,
+ 'no hour is not a deadline');
+assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { deadline_hours: 73 } }).valid,
+ 'more than seventy-two hours is not a deadline');
+assert.ok(!c.validate('ai.run-request@1', { workflow: 'live.rundown.episode', input: { a: 1 }, options: { deadline_hours: 1.5 } }).valid,
+ 'a deadline is a whole number of hours');
+
+// ── The answer: the class it ran in, the tier it was priced on, the clamp ──
+// The run's own document has many parts (a provenance, a requester, a usage), so the three new members are
+// checked against the run's schema with the required list dropped: what matters here is that the members
+// exist, that they hold the right values, and that a run that does not carry them is still a valid run.
+const run = load('ai/run.v1.json');
+for (const name of ['latency_class', 'service_tier', 'latency_clamped']) {
+ assert.ok(run.properties[name], 'the run answers with ' + name);
+ assert.ok(!run.required.includes(name), name + ' is optional: a run that does not carry it stays a valid run');
+}
+const ajv = new Ajv({ strict: false, allErrors: true });
+addFormats(ajv);
+const member = (name) => ajv.compile(run.properties[name]);
+const latencyClass = member('latency_class');
+const serviceTier = member('service_tier');
+const latencyClamped = member('latency_clamped');
+assert.ok(latencyClass('batch'), 'a run may answer with the batch class');
+assert.ok(!latencyClass('now'), 'a class that does not exist is refused');
+for (const tier of ['standard', 'flex', 'batch', 'offpeak', 'local']) {
+ assert.ok(serviceTier(tier), 'the tier ' + tier + ' is a tier');
+}
+assert.ok(serviceTier(null), 'the tier is unknown until the run answers');
+assert.ok(!serviceTier('cheapest'), 'a tier that does not exist is refused');
+assert.ok(latencyClamped({ asked: 'interactive', used: 'background' }),
+ 'a clamped run says which class was asked for and which was used');
+assert.ok(!latencyClamped({ asked: 'interactive' }), 'a clamp without the class used is refused');
+assert.ok(!latencyClamped({ asked: 'now', used: 'background' }), 'a clamp names a class that exists');
+
+// ── The batches: many prompts, answered later ──────────────────────────────
+const batchDoc = {
+ id: 'bat_0123456789abcdef01234567', status: 'queued', owner_type: 'user', owner_id: 'usr_1', project_id: null,
+ item_count: 2, items: [], discount: '500000', deadline_at: '2026-11-08T22:58:00.000Z',
+ created_at: '2026-11-07T22:58:00.000Z', updated_at: '2026-11-07T22:58:00.000Z',
+};
+assert.ok(c.validate('ai.batch@1', batchDoc).valid, 'a batch is a queue with a deadline');
+assert.ok(c.validate('ai.batch-read-result@1', { batches: [batchDoc] }).valid, 'the list of batches answers with batches');
+assert.ok(c.validate('ai.batch-read-result@1', { batch: batchDoc }).valid, 'one batch answers with a batch');
+assert.ok(!c.validate('ai.batch-read-result@1', { batch: batchDoc, batches: [] }).valid, 'the read answers one way or the other');
+assert.ok(c.validate('ai.batch-create-request@1', { items: ['one prompt', { prompt: 'another', model: 'gpt-4o-mini' }] }).valid,
+ 'a batch carries prompts');
+assert.ok(c.validate('ai.batch-create-request@1', { items: ['a prompt'], deadline_hours: 24 }).valid, 'a batch may ask for a day');
+assert.ok(!c.validate('ai.batch-create-request@1', { items: ['a prompt'], deadline_hours: 100 }).valid,
+ 'a batch deadline outside 1 to 72 hours is refused');
+assert.ok(!c.validate('ai.batch-create-request@1', { items: [] }).valid, 'a batch without prompts is refused');
+assert.ok(c.validate('ai.batch-create-result@1', { batch: batchDoc }).valid, 'opening a batch answers with the batch');
+
+// ── The catalog and the grants ─────────────────────────────────────────────
+for (const id of ['ai.batch', 'ai.batch-item', 'ai.batch-create-request', 'ai.batch-create-result', 'ai.batch-read-result']) {
+ assert.ok(c.catalog.some(e => e.id === id), id + ' is in the catalog');
+}
+assert.ok(c.capabilities.get('ai.batches.create'), 'the grant that opens a batch exists');
+assert.ok(c.capabilities.get('ai.batches.read'), 'the grant that reads batches exists');
+assert.ok(c.services.get('ai').capabilities.includes('ai.batches.create'), 'the AI service offers the grant that opens a batch');
+assert.ok(c.services.get('ai').capabilities.includes('ai.batches.read'), 'the AI service offers the grant that reads batches');
@@ -960,6 +960,7 @@ sub(path.join(__dirname, 'billing-grace.test.js'));
@@ -960,6 +960,7 @@ sub(path.join(__dirname, 'billing-grace.test.js'));
sub(path.join(__dirname, 'resources.test.js'));
sub(path.join(__dirname, 'resources.test.js'));
// The wait record (plan A2): a wait is a record, not a running loop.
// The wait record (plan A2): a wait is a record, not a running loop.
sub(path.join(__dirname, 'actor-wait.test.js'));
sub(path.join(__dirname, 'actor-wait.test.js'));
+sub(path.join(__dirname, 'batching-latency.test.js'));
sub(path.join(__dirname, 'network-consent.test.js'));
sub(path.join(__dirname, 'network-consent.test.js'));
// The machines hub (plan L34): one catalogue of roles, declared by the products that own them.
// The machines hub (plan L34): one catalogue of roles, declared by the products that own them.
sub(path.join(__dirname, 'machines.test.js'));
sub(path.join(__dirname, 'machines.test.js'));