# impartire API — Reference: EPG schedules

Every path is under `https://impartire.com/api/v1`. The machine-readable contract is https://impartire.com/api/v1/openapi.json (OpenAPI 3.1).

#### GET /api/v1/schedules

List a service's schedule in a window

The programme slots of one service that overlap [from, to) — at most 31 days — by start time. Each slot carries its readiness as last evaluated (reading does not re-evaluate it). Poll a window with `updated_after` (exclusive, millisecond); a deleted slot simply stops appearing. For an OAuth token, its person needs membership of the org (no platform-admin bypass); a key reads the whole org. 404 when the org does not have the EPG, and for an OAuth token whose person is not a member of the org. Also 404 for a service that is not the org's.

Scopes: `epg:read`

Parameters:

- `service_id` (query, string (uuid), required) — The service whose schedule to read (GET /api/v1/services lists them). Required.
- `from` (query, string (date-time), required) — The start of the window (ISO 8601 with offset). Required.
- `to` (query, string (date-time), required) — The end of the window (ISO 8601 with offset), after `from` and at most 31 days after it. Required. Slots overlapping [from, to) are listed.
- `updated_after` (query, string (date-time)) — Only slots changed strictly after this instant (ISO 8601 with offset), compared to the millisecond.
- `limit` (query, integer) — Page size, default 50. Values above 100 are treated as 100.
- `cursor` (query, string) — next_cursor from the previous page, with the other parameters unchanged.

Response 200 — `ScheduleSlotList`

- `data` (array of ScheduleSlot, required) — By `starts_at`, then id.
  - `id` (string (uuid), required)
  - `service` (object, required) — The service the programme airs on.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `title` (string, required)
  - `description` (string or null, required)
  - `starts_at` (string (date-time), required)
  - `ends_at` (string (date-time), required)
  - `asset` (object or null, required) — The asset scheduled, by its root id (GET /api/v1/assets/{id} takes it); `name` is the version picked when it was scheduled. Null for a rotation slot or when the asset is deleted or not in this org.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `file` (object or null, required) — The file that airs ({ id, name: its filename }), pinned when the asset was scheduled so a later upload cannot change what airs; null when none is pinned, or the pinned file is not an asset file of this org.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `source` (object or object or null, required) — Where a rotation slot draws its programme from — `{ project }` or `{ collection }`; null for a slot with no source, or one whose source is deleted or not in this org.
    - `project` (object, required)
      - `id` (string (uuid), required)
      - `name` (string or null, required) — The project's or collection's name. Null — for an OAuth token — when its person may not read it (a project they cannot open; a collection outside the projects they can open, or not shared with them): the id is the slot's own data, the name is not disclosed.
  - `format` (object or null, required) — The clock the slot was authored against (GET /api/v1/formats/{id}), frozen on the slot; null for a freeform slot.
    - `id` (string (uuid), required)
    - `name` (string, required)
  - `readiness` ("pending" | "ready" | "not_ready" | "unknown", required) — Whether what is scheduled meets the service's transmission spec, as last evaluated (when the slot is made, when its asset or service changes, and when its file is measured — not when you read it): `ready` — it meets the spec, or the service has no spec; `not_ready` — it fails the spec; `pending` — nothing to judge yet (no asset or no file), or a value the spec demands could not be measured; `unknown` — the file is not measured yet.
  - `created_at` (string (date-time), required)
  - `updated_at` (string (date-time), required)
- `next_cursor` (string or null, required) — Pass as `cursor` to get the next page; null means this is the last page.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 422 (Problem) — validation — a missing or bad `service_id`, `from` or `to`, a window wider than allowed, a bad filter, `limit` or `cursor` (`errors[].path` names it).
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### POST /api/v1/schedules

Put a programme on a service's schedule (guarded)

A guarded action (ADR 0081): it changes what airs. The app's own schedule write runs: every id the slot names must be the org's — the service, the format, the asset (by its current version's id or its root id), the source project or collection, and the pinned file must be a file of the asset (any version) — else 422 naming the field; the file that airs is pinned (`file_id`, or the asset's primary video); the slot's readiness is evaluated against the service's spec; the transmission log is re-derived and the on-air graphic re-pushed. An asset under legal hold is 409 `legal-hold`. **An API key's slot is made at once**: 201 with the slot and its ETag. **An OAuth token's is not**: 202 with a Confirmation naming the programme, the service, its airtime and what airs; once its person confirms it in impartire the slot is made as them (every check again), and the confirmation's `result` is the ScheduleSlot. Its person must be an org admin (the app's verb; no platform-admin bypass). Audited: the slot's capture and a `schedule.created` event. 404 when the org does not have the EPG, and for an OAuth token whose person is not a member of the org.

Scopes: `schedules:write`

Parameters:

- `Idempotency-Key` (header, string) — Makes a retry safe: the same key and request within 24 hours replays the first response. 1–255 characters.

Request body — `CreateScheduleSlotRequest`

- `service_id` (string (uuid), required) — The service (EPG channel) it airs on, from GET /api/v1/services.
- `title` (string, required)
- `description` (string or null)
- `starts_at` (string (date-time), required)
- `ends_at` (string (date-time), required) — After `starts_at`.
- `asset_id` (string (uuid) or null)
- `file_id` (string (uuid) or null) — Pin this file to air: a file of the asset (any of its versions). Needs `asset_id`. Omit to pin the asset's primary video.
- `source_project_id` (string (uuid) or null) — A rotation slot's source: a live project of this org. Not with `source_collection_id`.
- `source_collection_id` (string (uuid) or null) — A rotation slot's source: a live collection of this org, by its id or its root id. Not with `source_project_id`.
- `format_id` (string (uuid) or null) — The clock the slot is authored against (GET /api/v1/formats), frozen on the slot.
- `broadcast_day` (string (date)) — The broadcast day (YYYY-MM-DD) the slot belongs to, when it is not `starts_at`'s UTC date — a programme after midnight belongs to the day before. Stamped once; a change never re-stamps it.

Response 201 — `ScheduleSlot`: A programme on a service's schedule: what airs, when, and whether it is ready to air.

- `id` (string (uuid), required)
- `service` (object, required) — The service the programme airs on.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `title` (string, required)
- `description` (string or null, required)
- `starts_at` (string (date-time), required)
- `ends_at` (string (date-time), required)
- `asset` (object or null, required) — The asset scheduled, by its root id (GET /api/v1/assets/{id} takes it); `name` is the version picked when it was scheduled. Null for a rotation slot or when the asset is deleted or not in this org.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `file` (object or null, required) — The file that airs ({ id, name: its filename }), pinned when the asset was scheduled so a later upload cannot change what airs; null when none is pinned, or the pinned file is not an asset file of this org.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `source` (object or object or null, required) — Where a rotation slot draws its programme from — `{ project }` or `{ collection }`; null for a slot with no source, or one whose source is deleted or not in this org.
  - `project` (object, required)
    - `id` (string (uuid), required)
    - `name` (string or null, required) — The project's or collection's name. Null — for an OAuth token — when its person may not read it (a project they cannot open; a collection outside the projects they can open, or not shared with them): the id is the slot's own data, the name is not disclosed.
- `format` (object or null, required) — The clock the slot was authored against (GET /api/v1/formats/{id}), frozen on the slot; null for a freeform slot.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `readiness` ("pending" | "ready" | "not_ready" | "unknown", required) — Whether what is scheduled meets the service's transmission spec, as last evaluated (when the slot is made, when its asset or service changes, and when its file is measured — not when you read it): `ready` — it meets the spec, or the service has no spec; `not_ready` — it fails the spec; `pending` — nothing to judge yet (no asset or no file), or a value the spec demands could not be measured; `unknown` — the file is not measured yet.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required)

Response 202 — `Confirmation`: A guarded action an OAuth token asked for, waiting for its person to confirm it in impartire (ADR 0081). A guarded endpoint answers 202 with this and a Location; poll it until `status` is executed, declined or expired.

- `id` (string (uuid), required)
- `status` ("pending" | "confirmed" | "declined" | "expired" | "executed", required) — pending: waiting for the person to confirm or decline it in impartire (at `confirm_url`). confirmed: they confirmed and the action is running — poll again. executed: it ran; `result` holds its answer, or `problem` why it failed. declined: they said no; it will never run. expired: nobody decided within 15 minutes; it will never run (GET /api/v1/confirmations/{id} answers 410 confirmation-expired instead of this body; the person still sees it in impartire).
- `action` (string, required) — The guarded action, e.g. `run.signal`.
- `resource` (object, required)
  - `type` (string, required) — The kind of resource the action is on, e.g. `run`.
  - `id` (string (uuid) or null, required)
  - `name` (string, required) — The resource as the person was shown it when the action was asked for.
- `summary` (string, required) — The action in words, as the person reads it on the confirmation page.
- `client_id` (string, required) — The OAuth client that asked.
- `confirm_url` (string, required) — Where the person confirms or declines it. Show it to them; the API cannot confirm.
- `created_at` (string (date-time), required)
- `expires_at` (string (date-time), required) — 15 minutes after it was asked for. A pending confirmation past it is expired.
- `decided_at` (string (date-time) or null, required) — When the person confirmed or declined it, or when it expired; null while pending.
- `result` (any, required) — When executed and the action succeeded: the action's answer (what the endpoint would have returned with a key). Null otherwise.
- `problem` (Problem or null, required) — When executed and the action failed: the Problem it failed with. Null otherwise.
  - `type` (string, required) — A stable URL naming the problem: https://impartire.com/problems/<code>. Branch on this.
  - `title` (string, required)
  - `status` (integer, required)
  - `detail` (string, required) — What went wrong and what to do about it, written for the caller.
  - `instance` (string, required) — The request path.
  - `request_id` (string, required) — Also sent as the Request-Id header. Quote it when reporting a problem.
  - `errors` (array of object) — On 422 validation: one entry per invalid field.
    - `path` (string, required) — The field or parameter, as a dotted path.
    - `message` (string, required)
  - `edit_seq` (integer) — On 412 precondition-failed: the resource's current edit_seq.
  - `etag` (string) — On 412 precondition-failed for a project, a collection, a playlist or a schedule slot: its current ETag.
  - `item_id` (string) — On 409 already-member: the existing item's id (in the collection or playlist).
  - `schema_version` (integer) — On 409 type-changed: the asset type's current schema_version.
  - `confirmation_id` (string) — On 409 confirmation-pending: the confirmation already waiting for its person (GET /api/v1/confirmations/{id}).
  - `confirm_url` (string) — On 409 confirmation-pending: where the person confirms or declines it, in impartire.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — the token's person is not an org admin.
- 404 (Problem) — not-found.
- 409 (Problem) — legal-hold — the asset is held; confirmation-pending; or idempotency-in-flight.
- 422 (Problem) — validation (a bad field; `service_id`, `format_id`, `file_id`, `source_project_id` or `source_collection_id` not the org's; `ends_at` not after `starts_at`; both sources; `file_id` without `asset_id`), unknown-asset (`asset_id`), or idempotency-mismatch (an OAuth token may not send Idempotency-Key).
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### GET /api/v1/schedules/{id}

Get one schedule slot

One slot, in the list's shape. 404 when the org does not have the EPG, and for an OAuth token whose person is not a member of the org.

Scopes: `epg:read`

Parameters:

- `id` (path, string (uuid), required) — The slot's id, from GET /api/v1/schedules.
- `If-None-Match` (header, string) — An ETag from a previous response; 304 if unchanged.

Response 200 — `ScheduleSlot`: A programme on a service's schedule: what airs, when, and whether it is ready to air.

- `id` (string (uuid), required)
- `service` (object, required) — The service the programme airs on.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `title` (string, required)
- `description` (string or null, required)
- `starts_at` (string (date-time), required)
- `ends_at` (string (date-time), required)
- `asset` (object or null, required) — The asset scheduled, by its root id (GET /api/v1/assets/{id} takes it); `name` is the version picked when it was scheduled. Null for a rotation slot or when the asset is deleted or not in this org.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `file` (object or null, required) — The file that airs ({ id, name: its filename }), pinned when the asset was scheduled so a later upload cannot change what airs; null when none is pinned, or the pinned file is not an asset file of this org.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `source` (object or object or null, required) — Where a rotation slot draws its programme from — `{ project }` or `{ collection }`; null for a slot with no source, or one whose source is deleted or not in this org.
  - `project` (object, required)
    - `id` (string (uuid), required)
    - `name` (string or null, required) — The project's or collection's name. Null — for an OAuth token — when its person may not read it (a project they cannot open; a collection outside the projects they can open, or not shared with them): the id is the slot's own data, the name is not disclosed.
- `format` (object or null, required) — The clock the slot was authored against (GET /api/v1/formats/{id}), frozen on the slot; null for a freeform slot.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `readiness` ("pending" | "ready" | "not_ready" | "unknown", required) — Whether what is scheduled meets the service's transmission spec, as last evaluated (when the slot is made, when its asset or service changes, and when its file is measured — not when you read it): `ready` — it meets the spec, or the service has no spec; `not_ready` — it fails the spec; `pending` — nothing to judge yet (no asset or no file), or a value the spec demands could not be measured; `unknown` — the file is not measured yet.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required)

Other responses:

- 304 — Not modified — the ETag in If-None-Match is current.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope — the key lacks a required scope.
- 404 (Problem) — not-found.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### PATCH /api/v1/schedules/{id}

Move or change a schedule slot (guarded)

A guarded action (ADR 0081): it changes what airs. If-Match is required: the slot's ETag from the last GET (or the create), or its bare `v`. Send only the fields to change. The app's own write runs, with the same org checks as the create (422 naming the field); a new asset re-pins the file that airs and re-evaluates readiness, as does a move to another service; the transmission log is re-derived for the old and the new airtime and the on-air graphic re-pushed. Naming an asset under legal hold, or changing only the pinned file of a slot whose asset is held, is 409 `legal-hold`. **An API key's change happens at once** (200 with the slot and its new ETag). **An OAuth token's does not**: 202 with a Confirmation saying what moves or changes, by name; on confirm it applies only if the slot is still the version the person was shown (else the confirmation's `problem` is a 412). Its person must be an org admin. Audited: the slot's capture and a `schedule.updated` event. 404 when the org does not have the EPG, and for an OAuth token whose person is not a member of the org.

Scopes: `schedules:write`

Parameters:

- `id` (path, string (uuid), required) — The slot's id, from GET /api/v1/schedules.
- `If-Match` (header, string, required) — The slot's ETag, or its bare version.

Request body — `UpdateScheduleSlotRequest`

- `service_id` (string (uuid)) — Move it to another service of this org.
- `title` (string)
- `description` (string or null)
- `starts_at` (string (date-time))
- `ends_at` (string (date-time)) — After `starts_at` (the new one, or the slot's).
- `asset_id` (string (uuid) or null) — Another asset to air (its current version's id or its root id; the file is re-pinned), or null to make it a rotation slot.
- `file_id` (string (uuid) or null) — Pin this file to air: a file of the scheduled asset (any of its versions) — the one `asset_id` names, or the slot's. On a slot whose asset is under legal hold, a change of the file is 409 `legal-hold`.
- `source_project_id` (string (uuid) or null)
- `source_collection_id` (string (uuid) or null) — By its id or its root id.
- `format_id` (string (uuid) or null)

Response 200 — `ScheduleSlot`: A programme on a service's schedule: what airs, when, and whether it is ready to air.

- `id` (string (uuid), required)
- `service` (object, required) — The service the programme airs on.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `title` (string, required)
- `description` (string or null, required)
- `starts_at` (string (date-time), required)
- `ends_at` (string (date-time), required)
- `asset` (object or null, required) — The asset scheduled, by its root id (GET /api/v1/assets/{id} takes it); `name` is the version picked when it was scheduled. Null for a rotation slot or when the asset is deleted or not in this org.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `file` (object or null, required) — The file that airs ({ id, name: its filename }), pinned when the asset was scheduled so a later upload cannot change what airs; null when none is pinned, or the pinned file is not an asset file of this org.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `source` (object or object or null, required) — Where a rotation slot draws its programme from — `{ project }` or `{ collection }`; null for a slot with no source, or one whose source is deleted or not in this org.
  - `project` (object, required)
    - `id` (string (uuid), required)
    - `name` (string or null, required) — The project's or collection's name. Null — for an OAuth token — when its person may not read it (a project they cannot open; a collection outside the projects they can open, or not shared with them): the id is the slot's own data, the name is not disclosed.
- `format` (object or null, required) — The clock the slot was authored against (GET /api/v1/formats/{id}), frozen on the slot; null for a freeform slot.
  - `id` (string (uuid), required)
  - `name` (string, required)
- `readiness` ("pending" | "ready" | "not_ready" | "unknown", required) — Whether what is scheduled meets the service's transmission spec, as last evaluated (when the slot is made, when its asset or service changes, and when its file is measured — not when you read it): `ready` — it meets the spec, or the service has no spec; `not_ready` — it fails the spec; `pending` — nothing to judge yet (no asset or no file), or a value the spec demands could not be measured; `unknown` — the file is not measured yet.
- `created_at` (string (date-time), required)
- `updated_at` (string (date-time), required)

Response 202 — `Confirmation`: A guarded action an OAuth token asked for, waiting for its person to confirm it in impartire (ADR 0081). A guarded endpoint answers 202 with this and a Location; poll it until `status` is executed, declined or expired.

- `id` (string (uuid), required)
- `status` ("pending" | "confirmed" | "declined" | "expired" | "executed", required) — pending: waiting for the person to confirm or decline it in impartire (at `confirm_url`). confirmed: they confirmed and the action is running — poll again. executed: it ran; `result` holds its answer, or `problem` why it failed. declined: they said no; it will never run. expired: nobody decided within 15 minutes; it will never run (GET /api/v1/confirmations/{id} answers 410 confirmation-expired instead of this body; the person still sees it in impartire).
- `action` (string, required) — The guarded action, e.g. `run.signal`.
- `resource` (object, required)
  - `type` (string, required) — The kind of resource the action is on, e.g. `run`.
  - `id` (string (uuid) or null, required)
  - `name` (string, required) — The resource as the person was shown it when the action was asked for.
- `summary` (string, required) — The action in words, as the person reads it on the confirmation page.
- `client_id` (string, required) — The OAuth client that asked.
- `confirm_url` (string, required) — Where the person confirms or declines it. Show it to them; the API cannot confirm.
- `created_at` (string (date-time), required)
- `expires_at` (string (date-time), required) — 15 minutes after it was asked for. A pending confirmation past it is expired.
- `decided_at` (string (date-time) or null, required) — When the person confirmed or declined it, or when it expired; null while pending.
- `result` (any, required) — When executed and the action succeeded: the action's answer (what the endpoint would have returned with a key). Null otherwise.
- `problem` (Problem or null, required) — When executed and the action failed: the Problem it failed with. Null otherwise.
  - `type` (string, required) — A stable URL naming the problem: https://impartire.com/problems/<code>. Branch on this.
  - `title` (string, required)
  - `status` (integer, required)
  - `detail` (string, required) — What went wrong and what to do about it, written for the caller.
  - `instance` (string, required) — The request path.
  - `request_id` (string, required) — Also sent as the Request-Id header. Quote it when reporting a problem.
  - `errors` (array of object) — On 422 validation: one entry per invalid field.
    - `path` (string, required) — The field or parameter, as a dotted path.
    - `message` (string, required)
  - `edit_seq` (integer) — On 412 precondition-failed: the resource's current edit_seq.
  - `etag` (string) — On 412 precondition-failed for a project, a collection, a playlist or a schedule slot: its current ETag.
  - `item_id` (string) — On 409 already-member: the existing item's id (in the collection or playlist).
  - `schema_version` (integer) — On 409 type-changed: the asset type's current schema_version.
  - `confirmation_id` (string) — On 409 confirmation-pending: the confirmation already waiting for its person (GET /api/v1/confirmations/{id}).
  - `confirm_url` (string) — On 409 confirmation-pending: where the person confirms or declines it, in impartire.

Other responses:

- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — the token's person is not an org admin.
- 404 (Problem) — not-found.
- 409 (Problem) — legal-hold — the named asset is held, or only the file changes on a slot whose asset is held; or confirmation-pending.
- 412 (Problem) — precondition-failed — If-Match is stale; `etag` in the body is the current one.
- 422 (Problem) — validation (no field, a bad field, an id not the org's, `ends_at` not after `starts_at`, both sources, `file_id` on a slot with no asset), or unknown-asset (`asset_id`).
- 428 (Problem) — precondition-required — no If-Match.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.

#### DELETE /api/v1/schedules/{id}

Remove a schedule slot (guarded)

A guarded action (ADR 0081): it changes what airs. The slot leaves the schedule and its transmission log; the on-air graphic is re-pushed. No If-Match, and never refused for a legal hold — taking a programme off air is not held. **An API key's removal happens at once** (204). **An OAuth token's does not**: 202 with a Confirmation naming the programme, the service and its airtime; the confirmation's `result` is null. Its person must be an org admin. Audited: the slot's capture and a `schedule.deleted` event. 404 when the org does not have the EPG, and for an OAuth token whose person is not a member of the org.

Scopes: `schedules:write`

Parameters:

- `id` (path, string (uuid), required) — The slot's id, from GET /api/v1/schedules.

Response 202 — `Confirmation`: A guarded action an OAuth token asked for, waiting for its person to confirm it in impartire (ADR 0081). A guarded endpoint answers 202 with this and a Location; poll it until `status` is executed, declined or expired.

- `id` (string (uuid), required)
- `status` ("pending" | "confirmed" | "declined" | "expired" | "executed", required) — pending: waiting for the person to confirm or decline it in impartire (at `confirm_url`). confirmed: they confirmed and the action is running — poll again. executed: it ran; `result` holds its answer, or `problem` why it failed. declined: they said no; it will never run. expired: nobody decided within 15 minutes; it will never run (GET /api/v1/confirmations/{id} answers 410 confirmation-expired instead of this body; the person still sees it in impartire).
- `action` (string, required) — The guarded action, e.g. `run.signal`.
- `resource` (object, required)
  - `type` (string, required) — The kind of resource the action is on, e.g. `run`.
  - `id` (string (uuid) or null, required)
  - `name` (string, required) — The resource as the person was shown it when the action was asked for.
- `summary` (string, required) — The action in words, as the person reads it on the confirmation page.
- `client_id` (string, required) — The OAuth client that asked.
- `confirm_url` (string, required) — Where the person confirms or declines it. Show it to them; the API cannot confirm.
- `created_at` (string (date-time), required)
- `expires_at` (string (date-time), required) — 15 minutes after it was asked for. A pending confirmation past it is expired.
- `decided_at` (string (date-time) or null, required) — When the person confirmed or declined it, or when it expired; null while pending.
- `result` (any, required) — When executed and the action succeeded: the action's answer (what the endpoint would have returned with a key). Null otherwise.
- `problem` (Problem or null, required) — When executed and the action failed: the Problem it failed with. Null otherwise.
  - `type` (string, required) — A stable URL naming the problem: https://impartire.com/problems/<code>. Branch on this.
  - `title` (string, required)
  - `status` (integer, required)
  - `detail` (string, required) — What went wrong and what to do about it, written for the caller.
  - `instance` (string, required) — The request path.
  - `request_id` (string, required) — Also sent as the Request-Id header. Quote it when reporting a problem.
  - `errors` (array of object) — On 422 validation: one entry per invalid field.
    - `path` (string, required) — The field or parameter, as a dotted path.
    - `message` (string, required)
  - `edit_seq` (integer) — On 412 precondition-failed: the resource's current edit_seq.
  - `etag` (string) — On 412 precondition-failed for a project, a collection, a playlist or a schedule slot: its current ETag.
  - `item_id` (string) — On 409 already-member: the existing item's id (in the collection or playlist).
  - `schema_version` (integer) — On 409 type-changed: the asset type's current schema_version.
  - `confirmation_id` (string) — On 409 confirmation-pending: the confirmation already waiting for its person (GET /api/v1/confirmations/{id}).
  - `confirm_url` (string) — On 409 confirmation-pending: where the person confirms or declines it, in impartire.

Other responses:

- 204 — An API key's removal: the slot is gone.
- 401 (Problem) — unauthenticated — no key, or an unknown, revoked or expired one.
- 403 (Problem) — insufficient-scope, or insufficient-permission — the token's person is not an org admin.
- 404 (Problem) — not-found.
- 409 (Problem) — confirmation-pending.
- 429 (Problem) — rate-limited — the key's request budget is spent; wait Retry-After seconds.
- 500 (Problem) — internal.
