ProjectUsageResult network.project-usage-result@1
Generated at from
openvibe-contracts v0.76.0 and
openvibe-sdk v0.20.1.
- Version
- 1.0.0
- Owner
- network
- Visibility
- first-party
- Status
- active
- Compatibility
- backward
- Decision
- ADR-014
- Schema
https://openvibe.network/contracts/network/project-usage-result.v1.json
network.project-usage-result@1 (roadmap WS-N task 4, ADR-014): what GET /api/v1/projects/:project/usage?days=&env= on OpenVibe.Network answers a project's owner or admin (or staff): the project's usage per day, service, capability and unit, added up from the <service>.usage.recorded rollups (common.usage-recorded@1) the owning services emit after each hour closes; the project's recorded quotas with what the current window has used; and its recent failures with their codes and trace ids. Counts only, never who did what: no subject id, address, input or content.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
project_id | string | yes |
| |
env | enum | yes | The environments counted (the env query parameter; all by default). |
|
range | object | yes |
| |
range.days | integer | yes |
| |
range.from | string | yes | First UTC day counted. |
|
range.to | string | yes | Last UTC day counted (today). |
|
generated_at | string | yes |
| |
last_recorded_at | string | null | yes | When the newest rollup for this project arrived; null before the first. |
|
freshness | string | yes | How current the numbers are, in words (rollups arrive after each hour closes). |
|
totals | array of object | yes | The whole range per service, capability, unit and environment. |
|
totals[].service | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/service | yes | ||
totals[].capability | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/capability | yes | ||
totals[].unit | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/unit | yes | ||
totals[].env | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/env | yes | ||
totals[].quantity | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/count | yes | ||
totals[].errors | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/count | yes | ||
daily | array of object | yes | One row per UTC day, service, capability, dimension, unit and environment that saw usage; days without any are absent. Newest day first. |
|
daily[].day | string | yes |
| |
daily[].service | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/service | yes | ||
daily[].capability | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/capability | yes | ||
daily[].dimension | string | null | yes |
| |
daily[].unit | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/unit | yes | ||
daily[].env | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/env | yes | ||
daily[].quantity | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/count | yes | ||
daily[].errors | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/count | yes | ||
quotas | array of object | yes | The project's recorded quotas (GET /api/v1/projects/:project/quotas), each with what its current window has used, in every environment together. used is null where the rollups cannot tell (a minute or hour window, or a unit no service reports for that capability). |
|
quotas[].capability | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/capability | yes | ||
quotas[].limit | integer | yes |
| |
quotas[].window | enum | yes |
| |
quotas[].unit | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/unit | yes | ||
quotas[].enforced_by | string | null | yes | The audience of the service that enforces it (openvibe.<owner>). | |
quotas[].used | integer | null | yes |
| |
quotas[].remaining | integer | null | yes |
| |
quotas[].window_start | string | null | yes | Start of the current window counted (today, this month); null for total and when used is null. |
|
quotas[].note | string | null | yes |
| |
errors | object | yes |
| |
errors.total | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/count | yes | ||
errors.by_code | array of object | yes |
| |
errors.by_code[].service | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/service | yes | ||
errors.by_code[].capability | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/capability | yes | ||
errors.by_code[].code | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/code | yes | ||
errors.by_code[].count | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/count | yes | ||
errors.recent | array of object | yes | The newest failures the rollups sampled, newest first. |
|
errors.recent[].at | string | yes |
| |
errors.recent[].env | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/env | yes | ||
errors.recent[].service | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/service | yes | ||
errors.recent[].capability | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/capability | yes | ||
errors.recent[].code | https://openvibe.network/contracts/network/project-usage-result.v1.json#/$defs/code | yes | ||
errors.recent[].status | integer | null | yes |
| |
errors.recent[].trace_id | string | null | yes |
| |
errors.recent[].ref | string | null | yes |
|
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: dashboard
{
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"env": "all",
"range": {
"days": 30,
"from": "2026-08-28",
"to": "2026-09-26"
},
"generated_at": "2026-09-26T15:05:00.000Z",
"last_recorded_at": "2026-09-26T15:02:11.000Z",
"freshness": "Usage arrives as hourly rollups after each hour closes; the current hour is not counted yet.",
"totals": [
{
"service": "tools",
"capability": "tools.job.create",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"service": "events",
"capability": "events.app.publish",
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"daily": [
{
"day": "2026-09-26",
"service": "tools",
"capability": "tools.job.create",
"dimension": "img.process",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"day": "2026-09-26",
"service": "events",
"capability": "events.app.publish",
"dimension": null,
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"quotas": [
{
"capability": "tools.job.create",
"limit": 1000,
"window": "day",
"unit": "jobs",
"enforced_by": "openvibe.tools",
"used": 42,
"remaining": 958,
"window_start": "2026-09-26T00:00:00.000Z",
"note": null
},
{
"capability": "events.app.publish",
"limit": 120,
"window": "minute",
"unit": "events",
"enforced_by": "openvibe.events",
"used": null,
"remaining": null,
"window_start": null,
"note": "a minute window is enforced by the service as it happens; hourly rollups cannot show it"
}
],
"errors": {
"total": 64,
"by_code": [
{
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"count": 60
},
{
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.failed",
"count": 2
}
],
"recent": [
{
"at": "2026-09-26T14:51:07.412Z",
"env": "production",
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.timeout",
"status": 504,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"ref": "job_01JAB2C3D4E5F6G7H8J9K0MNPR"
},
{
"at": "2026-09-26T09:59:58.120Z",
"env": "sandbox",
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"status": 429,
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"ref": null
}
]
}
}Valid: nothing-yet
{
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"env": "production",
"range": {
"days": 7,
"from": "2026-09-20",
"to": "2026-09-26"
},
"generated_at": "2026-09-26T15:05:00.000Z",
"last_recorded_at": null,
"freshness": "No usage has been recorded for this project yet.",
"totals": [],
"daily": [],
"quotas": [],
"errors": {
"total": 0,
"by_code": [],
"recent": []
}
}Rejected: a-year
{
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"env": "all",
"range": {
"days": 365,
"from": "2026-08-28",
"to": "2026-09-26"
},
"generated_at": "2026-09-26T15:05:00.000Z",
"last_recorded_at": "2026-09-26T15:02:11.000Z",
"freshness": "Usage arrives as hourly rollups after each hour closes; the current hour is not counted yet.",
"totals": [
{
"service": "tools",
"capability": "tools.job.create",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"service": "events",
"capability": "events.app.publish",
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"daily": [
{
"day": "2026-09-26",
"service": "tools",
"capability": "tools.job.create",
"dimension": "img.process",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"day": "2026-09-26",
"service": "events",
"capability": "events.app.publish",
"dimension": null,
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"quotas": [
{
"capability": "tools.job.create",
"limit": 1000,
"window": "day",
"unit": "jobs",
"enforced_by": "openvibe.tools",
"used": 42,
"remaining": 958,
"window_start": "2026-09-26T00:00:00.000Z",
"note": null
},
{
"capability": "events.app.publish",
"limit": 120,
"window": "minute",
"unit": "events",
"enforced_by": "openvibe.events",
"used": null,
"remaining": null,
"window_start": null,
"note": "a minute window is enforced by the service as it happens; hourly rollups cannot show it"
}
],
"errors": {
"total": 64,
"by_code": [
{
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"count": 60
},
{
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.failed",
"count": 2
}
],
"recent": [
{
"at": "2026-09-26T14:51:07.412Z",
"env": "production",
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.timeout",
"status": 504,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"ref": "job_01JAB2C3D4E5F6G7H8J9K0MNPR"
},
{
"at": "2026-09-26T09:59:58.120Z",
"env": "sandbox",
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"status": 429,
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"ref": null
}
]
}
}Rejected: negative-use
{
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"env": "all",
"range": {
"days": 30,
"from": "2026-08-28",
"to": "2026-09-26"
},
"generated_at": "2026-09-26T15:05:00.000Z",
"last_recorded_at": "2026-09-26T15:02:11.000Z",
"freshness": "Usage arrives as hourly rollups after each hour closes; the current hour is not counted yet.",
"totals": [
{
"service": "tools",
"capability": "tools.job.create",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"service": "events",
"capability": "events.app.publish",
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"daily": [
{
"day": "2026-09-26",
"service": "tools",
"capability": "tools.job.create",
"dimension": "img.process",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"day": "2026-09-26",
"service": "events",
"capability": "events.app.publish",
"dimension": null,
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"quotas": [
{
"capability": "tools.job.create",
"limit": 1000,
"window": "day",
"unit": "jobs",
"enforced_by": "openvibe.tools",
"used": -5,
"remaining": 958,
"window_start": "2026-09-26T00:00:00.000Z",
"note": null
},
{
"capability": "events.app.publish",
"limit": 120,
"window": "minute",
"unit": "events",
"enforced_by": "openvibe.events",
"used": null,
"remaining": null,
"window_start": null,
"note": "a minute window is enforced by the service as it happens; hourly rollups cannot show it"
}
],
"errors": {
"total": 64,
"by_code": [
{
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"count": 60
},
{
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.failed",
"count": 2
}
],
"recent": [
{
"at": "2026-09-26T14:51:07.412Z",
"env": "production",
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.timeout",
"status": 504,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"ref": "job_01JAB2C3D4E5F6G7H8J9K0MNPR"
},
{
"at": "2026-09-26T09:59:58.120Z",
"env": "sandbox",
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"status": 429,
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"ref": null
}
]
}
}Rejected: recent-error-names-a-person
{
"project_id": "prj_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"env": "all",
"range": {
"days": 30,
"from": "2026-08-28",
"to": "2026-09-26"
},
"generated_at": "2026-09-26T15:05:00.000Z",
"last_recorded_at": "2026-09-26T15:02:11.000Z",
"freshness": "Usage arrives as hourly rollups after each hour closes; the current hour is not counted yet.",
"totals": [
{
"service": "tools",
"capability": "tools.job.create",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"service": "events",
"capability": "events.app.publish",
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"daily": [
{
"day": "2026-09-26",
"service": "tools",
"capability": "tools.job.create",
"dimension": "img.process",
"unit": "jobs",
"env": "production",
"quantity": 42,
"errors": 3
},
{
"day": "2026-09-26",
"service": "events",
"capability": "events.app.publish",
"dimension": null,
"unit": "events",
"env": "sandbox",
"quantity": 1800,
"errors": 61
}
],
"quotas": [
{
"capability": "tools.job.create",
"limit": 1000,
"window": "day",
"unit": "jobs",
"enforced_by": "openvibe.tools",
"used": 42,
"remaining": 958,
"window_start": "2026-09-26T00:00:00.000Z",
"note": null
},
{
"capability": "events.app.publish",
"limit": 120,
"window": "minute",
"unit": "events",
"enforced_by": "openvibe.events",
"used": null,
"remaining": null,
"window_start": null,
"note": "a minute window is enforced by the service as it happens; hourly rollups cannot show it"
}
],
"errors": {
"total": 64,
"by_code": [
{
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"count": 60
},
{
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.failed",
"count": 2
}
],
"recent": [
{
"at": "2026-09-26T14:51:07.412Z",
"env": "production",
"service": "tools",
"capability": "tools.job.create",
"code": "tools.job.timeout",
"status": 504,
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"ref": "job_01JAB2C3D4E5F6G7H8J9K0MNPR",
"subject": {
"type": "user",
"id": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
}
},
{
"at": "2026-09-26T09:59:58.120Z",
"env": "sandbox",
"service": "events",
"capability": "events.app.publish",
"code": "events.quota_exceeded",
"status": 429,
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"ref": null
}
]
}
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('network.project-usage-result@1', value); // { valid, errors: [{ path, message }] }