MediaJobQueuedPayload media.job.queued@1
Generated at from
openvibe-contracts v0.33.0 and
openvibe-sdk v0.5.0.
- Version
- 1.0.0
- Owner
- media
- Visibility
- first-party
- Status
- active
- Compatibility
- backward
- Decision
- ADR-004
- Schema
https://openvibe.network/contracts/events/payloads/media.job.queued.v1.json
media.job.queued v1 (OpenVibe.Media server/jobs/queue.js enqueue, approve; server/events.js recordJob). A job was accepted and queued: a new job (POST /api/v2/:app/jobs), or a proposal its owner approved (decided_at). An Idempotency-Key replay, or a request joined to an identical job that is still active, creates no job and no event. A failed attempt that is queued again is media.job.retrying, not this. Emitted in the SQLite transaction that records the state change, so the event exists if and only if the change committed; it goes to OpenVibe.Events only (no app webhook). The payload is an event projection of the job (queue.jobEvent): its identity, state, counters, stable error_code, times and has_result. Deliberately left out, because events travel beyond the tenant: the tenant's params (object.split keeps whatever the caller sent), the caller's idempotency_key, the free-text error (raw handler or ffmpeg output), the result (thumbnail.regenerate's is the thumbnail URL, of a private VOD too), created_by/decided_by and owner_user_id (tenant-local user ids), and the handler's checkpoint, lease and request hash. GET the job with a tenant token for params/result/error (GET /api/v2/:app/jobs/:id with a token for app_id; has_result says whether there is a result to fetch). Times are ISO 8601 UTC (YYYY-MM-DDTHH:MM:SS.sssZ) or null. Jobs of developer-project sandbox tenants are not announced. Envelope: subject { type: job, id: <id> }, visibility internal, priority low, actor service:media.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
id | string | yes | The job. GET /api/v2/:app/jobs/:id with a token for app_id answers the rest of it. |
|
app_id | string | yes | The tenant that owns the job: an app (live, tools…) or a developer project's production tenant (prj_…). |
|
object_id | string | null | yes | The object the job works on; null for a tenant-wide job (invariant.scan). |
|
type | string | yes | Job type: thumbnail.regenerate, invariant.scan, object.split or object.remux. |
|
status | const | yes |
| |
attempts | integer | yes | Attempts started so far. |
|
max_attempts | integer | yes |
| |
error_code | null | yes | ||
cancel_requested | boolean | yes | Its owner asked a running job to stop. | |
has_result | const | yes | A job that never ran has no result. |
|
run_after | string | null | yes | A queued job waits until then (retry backoff); null otherwise. |
|
decided_at | string | null | yes | When its owner approved it, or cancelled it while it was proposed or queued; null otherwise. |
|
created_at | string | yes |
| |
updated_at | string | yes |
| |
started_at | null | yes | ||
finished_at | null | yes |
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: approved-proposal
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"type": "object.split",
"status": "queued",
"attempts": 0,
"max_attempts": 3,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": "2026-09-23T18:10:40.000Z",
"created_at": "2026-09-23T18:02:11.000Z",
"updated_at": "2026-09-23T18:10:40.000Z",
"started_at": null,
"finished_at": null
}Valid: tenant-wide-scan
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": null,
"type": "invariant.scan",
"status": "queued",
"attempts": 0,
"max_attempts": 3,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": null,
"created_at": "2026-09-23T18:02:11.000Z",
"updated_at": "2026-09-23T18:02:11.000Z",
"started_at": null,
"finished_at": null
}Valid: thumbnail
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"type": "thumbnail.regenerate",
"status": "queued",
"attempts": 0,
"max_attempts": 2,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": null,
"created_at": "2026-09-23T18:02:11.000Z",
"updated_at": "2026-09-23T18:02:11.000Z",
"started_at": null,
"finished_at": null
}Rejected: already-ran
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"type": "object.split",
"status": "queued",
"attempts": 1,
"max_attempts": 3,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": null,
"created_at": "2026-09-23T18:02:11.000Z",
"updated_at": "2026-09-23T18:02:11.000Z",
"started_at": "2026-09-23T18:02:12.000Z",
"finished_at": null
}Rejected: carries-idempotency-key
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"type": "object.split",
"status": "queued",
"attempts": 0,
"max_attempts": 3,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": null,
"created_at": "2026-09-23T18:02:11.000Z",
"updated_at": "2026-09-23T18:02:11.000Z",
"started_at": null,
"finished_at": null,
"idempotency_key": "thumb-4211-2026-09-23"
}Rejected: carries-owner-user
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"type": "thumbnail.regenerate",
"status": "queued",
"attempts": 0,
"max_attempts": 3,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": null,
"created_at": "2026-09-23T18:02:11.000Z",
"updated_at": "2026-09-23T18:02:11.000Z",
"started_at": null,
"finished_at": null,
"created_by": "app:live:user:57",
"owner_user_id": 57
}Rejected: sqlite-timestamps
{
"id": "mjob_01JAB3C4D5E6F7G8H9J0K1MNPQ",
"app_id": "live",
"object_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"type": "object.split",
"status": "queued",
"attempts": 0,
"max_attempts": 3,
"error_code": null,
"cancel_requested": false,
"has_result": false,
"run_after": null,
"decided_at": null,
"created_at": "2026-09-23 18:02:11",
"updated_at": "2026-09-23 18:02:11",
"started_at": null,
"finished_at": null
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('media.job.queued@1', value); // { valid, errors: [{ path, message }] }