{"openapi":"3.1.0","jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","info":{"title":"Personal Academy API","version":"v1","description":"Personal Academy public REST API — the `all` module, version 1.\n\n## Auth\nEvery request carries `Authorization: Bearer <api key>`. There is no session/cookie path for a machine caller. A request may fail with `401 unauthorized` (missing/invalid key), `403 scope_not_granted` (key not scoped for this module), `403 module_not_entitled` (the tenant's plan does not currently include it — checked live, every request, independent of the key), or `403 permission_denied` (the principal the key acts as lacks the permission, recomputed live from that principal's current role — never a snapshot taken when the key was minted). The three endpoints under \"meta\" need no key at all.\n\n## Pagination\nList endpoints are cursor-paginated (keyset, not offset). Pass `?cursor=` (from the previous response's `meta.cursor.next`) to fetch the next page; `null` means last page. `?sort=<field>` / `?sort=-<field>` selects the sort column from each endpoint's whitelist; an unrecognised value falls back to the endpoint's default instead of erroring.\n\n## Rate limits\nEvery response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (a delta in seconds) and `RateLimit-Policy`; a 429 adds `Retry-After` and a `retry_after_seconds` field in the body. The published baseline, and your organisation's own effective ceiling, are readable at `GET /api/v1/limits` — the numbers there come from the same row the limiter enforces, not from a copy. Requests refused before the limiter runs (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. If the limiter itself is unavailable, requests are allowed through rather than blocked (fail open — a quota outage should not become a full outage; contrast with auth/scope/permission checks, which fail closed) and the `RateLimit-*` headers are omitted rather than filled with a guess.\n\n## Versioning and deprecation\nThe lifecycle of every version is published at `GET /api/v1/versions` and enforced by the API itself. While a version is `deprecated`, every response carries RFC 8594's `Deprecation` and `Sunset` headers plus `Link: <policy>; rel=\"deprecation\"`. Once it is `sunset` every request answers `410 Gone` with a body naming `replacement_version`. You do not have to read a changelog to find out: the headers arrive on calls you are already making.\n\nMay change without a new version: adding an optional response field, adding a new endpoint, adding a new filter/sort column, adding a new module. May **not** change without shipping `/api/v2`: removing or renaming a field a client already receives, changing a field's type or meaning, tightening what a previously-permitted request accepts (e.g. making an optional filter required), or changing what an existing status code means.\n\nCombined document for every module currently built: learn, assess, org, skills, paths, events, insights, meta, partner, ai, authoring, programmes, social, knowledge, podcasts, engage, me, development, commerce, admin, hooks. A tenant licensed for only some of these should be handed the per-module document (GET /api/v1/<module>/openapi.json), not this one.","x-api-lifecycle":{"versions_url":"/api/v1/versions","limits_url":"/api/v1/limits","graphql_url":"/api/v1/graphql","graphql_schema_url":"/api/v1/graphql/schema.graphql","documentation_url":"/developers"}},"servers":[{"url":"/"}],"tags":[{"name":"learn"},{"name":"assess"},{"name":"org"},{"name":"skills"},{"name":"paths"},{"name":"events"},{"name":"insights"},{"name":"meta"},{"name":"partner"},{"name":"ai"},{"name":"authoring"},{"name":"programmes"},{"name":"social"},{"name":"knowledge"},{"name":"podcasts"},{"name":"engage"},{"name":"me"},{"name":"development"},{"name":"commerce"},{"name":"admin"},{"name":"hooks"},{"name":"embed"}],"x-tagGroups":[{"name":"Partners","tags":["partner","embed"]},{"name":"Platform API","tags":["learn","assess","org","skills","paths","events","insights","meta","ai","authoring","programmes","social","knowledge","podcasts","engage","me","development","commerce","admin","hooks"]}],"paths":{"/api/v1/admin/audit-events":{"get":{"summary":"List audit events","description":"The organisation's audit trail, newest first. Filter by actor, action prefix, target type and date range; follow `meta.cursor.next` for older events.\n\nRequires scope `module.governance` and permission `audit.view` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"actor","in":"query","required":false,"description":"Only events by this user id.","schema":{"type":"string","format":"uuid","description":"Only events by this user id."}},{"name":"action","in":"query","required":false,"description":"Action prefix, e.g. `role.` or `group.created`.","schema":{"type":"string","description":"Action prefix, e.g. `role.` or `group.created`."}},{"name":"target_type","in":"query","required":false,"description":"Only events on this kind of record.","schema":{"type":"string","description":"Only events on this kind of record."}},{"name":"since","in":"query","required":false,"description":"Events at or after this time.","schema":{"type":"string","format":"date-time","description":"Events at or after this time."}},{"name":"until","in":"query","required":false,"description":"Events at or before this time.","schema":{"type":"string","format":"date-time","description":"Events at or before this time."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAudit-eventsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/automation-rules":{"get":{"summary":"List automation rules","description":"The organisation's automation rules, newest first.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"is_active","in":"query","required":false,"description":"Only running (true) or paused (false) rules.","schema":{"type":"boolean","description":"Only running (true) or paused (false) rules."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAutomation-rulesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Create an automation rule","description":"Creates a rule. `trigger` and `action` are validated against the automation catalogue. An `assign_course` action needs the key to hold course.assign as well.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminAutomation-rulesItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name."},"description":{"type":"string","description":"What it is for."},"trigger":{"type":"object","description":"The firing event."},"action":{"type":"object","description":"What it does."},"is_active":{"type":"boolean","description":"Defaults to true."}},"required":["name","description","trigger","action","is_active"]}}}}}},"/api/v1/admin/automation-rules/{id}":{"delete":{"summary":"Delete an automation rule","description":"Deletes the rule and its run history.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Rule id.","schema":{"type":"string","format":"uuid","description":"Rule id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminAutomation-rulesIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get an automation rule","description":"One rule.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Rule id.","schema":{"type":"string","format":"uuid","description":"Rule id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminAutomation-rulesIdGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update an automation rule","description":"Change the name, description, active flag, trigger or action. Trigger and action are re-validated.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Rule id.","schema":{"type":"string","format":"uuid","description":"Rule id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminAutomation-rulesIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Rule name."},"description":{"type":"string","description":"What it is for."},"is_active":{"type":"boolean","description":"Run or pause."},"trigger":{"type":"object","description":"New trigger."},"action":{"type":"object","description":"New action."}},"required":["name","description","is_active","trigger","action"]}}}}}},"/api/v1/admin/automation-rules/{id}/runs":{"get":{"summary":"List a rule's runs","description":"Execution history for one rule, newest first: one row per attempted firing (success, failed, or skipped when the chain was too deep or already visited).\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Rule id.","schema":{"type":"string","format":"uuid","description":"Rule id."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminAutomation-rulesIdRunsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/channels":{"get":{"summary":"List delivery channels","description":"Slack, Teams and Google Chat channels. Secrets are write-only: only hints and `*_set` flags are returned.\n\nRequires scope `module.integrations` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"type","in":"query","required":false,"description":"Only channels of this kind.","schema":{"type":"string","enum":["slack","teams","google_chat"],"description":"Only channels of this kind."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminChannelsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Create a delivery channel","description":"Creates a channel from an incoming-webhook URL. The URL is validated against the destination guard, stored encrypted and never returned.\n\nRequires scope `module.integrations` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminChannelsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The channel encryption key is not configured on this server.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["slack","teams","google_chat"],"description":"slack, teams or google_chat."},"name":{"type":"string","description":"Display name."},"url":{"type":"string","description":"The incoming-webhook URL (write-only)."}},"required":["type","name","url"]}}}}}},"/api/v1/admin/channels/{id}":{"delete":{"summary":"Delete a delivery channel","description":"Deletes the channel and its stored secrets.\n\nRequires scope `module.integrations` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Channel id.","schema":{"type":"string","format":"uuid","description":"Channel id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminChannelsIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get a delivery channel","description":"One channel. Secrets are never returned.\n\nRequires scope `module.integrations` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Channel id.","schema":{"type":"string","format":"uuid","description":"Channel id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminChannelsIdGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update a delivery channel","description":"Rename, enable or disable, rotate the webhook URL, or set workspace-bot credentials. Secret fields are write-only.\n\nRequires scope `module.integrations` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Channel id.","schema":{"type":"string","format":"uuid","description":"Channel id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminChannelsIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Display name."},"is_active":{"type":"boolean","description":"Enable or disable."},"url":{"type":"string","description":"New webhook URL (write-only)."},"dm_enabled":{"type":"boolean","description":"Direct messages on or off."},"bot_token":{"type":"string","description":"Slack bot token (write-only)."},"app_id":{"type":"string","description":"Teams app id."},"app_secret":{"type":"string","description":"Teams app secret (write-only)."},"azure_tenant_id":{"type":"string","description":"Teams Azure tenant id."}},"required":["name","is_active","url","dm_enabled","bot_token","app_id","app_secret","azure_tenant_id"]}}}}}},"/api/v1/admin/channels/{id}/test":{"post":{"summary":"Send a test message to a channel","description":"Posts a fixed test notification to the channel and records the outcome on it (`last_status`). A channel that is disabled, or whose destination is refused, answers 422 with the reason.\n\nRequires scope `module.integrations` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Channel id.","schema":{"type":"string","format":"uuid","description":"Channel id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminChannelsIdTestItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The message could not be delivered; `reason` says why.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/departments":{"get":{"summary":"List departments","description":"Every department, team and location the key principal can see, as a flat list; build the tree from `parent_id`.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"type","in":"query","required":false,"description":"Only nodes of this type.","schema":{"type":"string","enum":["vertical","department","team","location"],"description":"Only nodes of this type."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminDepartmentsItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Create a department","description":"Creates a node in the organisation tree.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminDepartmentsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name."},"parent_id":{"type":["string","null"],"format":"uuid","description":"Parent node."},"type":{"type":"string","enum":["vertical","department","team","location"],"description":"Defaults to department."},"color":{"type":"string","description":"Display colour."},"description":{"type":"string","description":"Description."}},"required":["name","type","color","description"]}}}}}},"/api/v1/admin/departments/{id}":{"delete":{"summary":"Delete a department","description":"Deletes the node. The database refuses (409 conflict) while the node is still referenced.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Department id.","schema":{"type":"string","format":"uuid","description":"Department id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminDepartmentsIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get a department","description":"One node of the organisation tree.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Department id.","schema":{"type":"string","format":"uuid","description":"Department id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminDepartmentsIdGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update a department","description":"Rename, re-describe, reparent or retype. A change that would make a node its own ancestor is refused.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Department id.","schema":{"type":"string","format":"uuid","description":"Department id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminDepartmentsIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Name."},"description":{"type":["string","null"],"description":"Description."},"parent_id":{"type":["string","null"],"format":"uuid","description":"New parent (null makes it a root)."},"type":{"type":"string","enum":["vertical","department","team","location"],"description":"Level label."},"color":{"type":"string","description":"Display colour."}},"required":["name","type","color"]}}}}}},"/api/v1/admin/email-templates":{"get":{"summary":"List email templates","description":"Every email template kind with the organisation's override applied.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminEmail-templatesItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Create or replace an email template","description":"Sets the organisation's override for one kind. `kind` must be one of the built-in kinds; subject and body_markdown are required.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminEmail-templatesItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["compliance.deadline_due","compliance.overdue","compliance.team_overdue","invitation","role.changed"],"description":"The template kind."},"subject":{"type":"string","description":"Subject line."},"body_markdown":{"type":"string","description":"Body in Markdown."}},"required":["kind","subject","body_markdown"]}}}}}},"/api/v1/admin/email-templates/{kind}":{"delete":{"summary":"Reset an email template","description":"Removes the override so the built-in default applies again.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"kind","in":"path","required":true,"description":"The template kind.","schema":{"type":"string","enum":["compliance.deadline_due","compliance.overdue","compliance.team_overdue","invitation","role.changed"],"description":"The template kind."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminEmail-templatesKindItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get an email template","description":"One template kind with the override applied.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"kind","in":"path","required":true,"description":"The template kind.","schema":{"type":"string","enum":["compliance.deadline_due","compliance.overdue","compliance.team_overdue","invitation","role.changed"],"description":"The template kind."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminEmail-templatesKindItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Set an email template","description":"Creates or replaces the override for this kind.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"kind","in":"path","required":true,"description":"The template kind.","schema":{"type":"string","enum":["compliance.deadline_due","compliance.overdue","compliance.team_overdue","invitation","role.changed"],"description":"The template kind."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminEmail-templatesKindItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","description":"Subject line."},"body_markdown":{"type":"string","description":"Body in Markdown."}},"required":["subject","body_markdown"]}}}}}},"/api/v1/admin/email-templates/{kind}/test-send":{"post":{"summary":"Send a test email","description":"Sends the template, with sample values, to the key's own administrator address only. There is no recipient parameter.\n\nRequires scope `module.platform` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"kind","in":"path","required":true,"description":"The template kind.","schema":{"type":"string","description":"The template kind."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminEmail-templatesKindTest-sendItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The acting administrator has no deliverable address.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The mail provider refused the send.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/groups":{"get":{"summary":"List groups","description":"Static and dynamic groups, newest first.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring of the name.","schema":{"type":"string","description":"Case-insensitive substring of the name."}},{"name":"is_dynamic","in":"query","required":false,"description":"true for dynamic groups, false for static.","schema":{"type":"boolean","description":"true for dynamic groups, false for static."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminGroupsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Create a group","description":"Creates a static group (no members yet) or a dynamic one (`is_dynamic: true` with a `rule`). A static group cannot carry a rule.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Group name."},"description":{"type":"string","description":"Description."},"is_dynamic":{"type":"boolean","description":"Dynamic group?"},"rule":{"type":["object","null"],"description":"Required when is_dynamic; see lib/group-rules.ts."},"owner_department_id":{"type":["string","null"],"format":"uuid","description":"Owning department."}},"required":["name","description","is_dynamic"]}}}}}},"/api/v1/admin/groups/{id}":{"delete":{"summary":"Delete a group","description":"Deletes the group and its stored memberships. People are untouched.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id.","schema":{"type":"string","format":"uuid","description":"Group id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get a group","description":"One group.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id.","schema":{"type":"string","format":"uuid","description":"Group id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsIdGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update a group","description":"Rename, re-describe, change the owning department or (for a dynamic group) the rule. A static group cannot take a rule.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id.","schema":{"type":"string","format":"uuid","description":"Group id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Group name."},"description":{"type":"string","description":"Description."},"rule":{"type":"object","description":"New rule (dynamic groups only)."},"owner_department_id":{"type":["string","null"],"format":"uuid","description":"Owning department."}},"required":["name","description","rule"]}}}}}},"/api/v1/admin/groups/{id}/members":{"delete":{"summary":"Remove group members","description":"Removes people from a static group.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id.","schema":{"type":"string","format":"uuid","description":"Group id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsIdMembersItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_ids":{"type":"array","description":"Up to 500 user ids."}},"required":["user_ids"]}}}}},"get":{"summary":"List group members","description":"The people in the group (computed for a dynamic group). Needs settings.manage.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id.","schema":{"type":"string","format":"uuid","description":"Group id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsIdMembersGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Add group members","description":"Adds people to a static group. Each person is checked against your delegated scope; the response lists the outcome per person.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id.","schema":{"type":"string","format":"uuid","description":"Group id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminGroupsIdMembersPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_ids":{"type":"array","description":"Up to 500 user ids."}},"required":["user_ids"]}}}}}},"/api/v1/admin/legal-holds":{"get":{"summary":"List legal holds","description":"Open legal holds, newest first. Pass `include_released=true` to include released ones.\n\nRequires scope `module.governance` and permission `audit.view` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"include_released","in":"query","required":false,"description":"Include released holds.","schema":{"type":"boolean","description":"Include released holds."}},{"name":"subject_type","in":"query","required":false,"description":"Only holds on this kind of subject.","schema":{"type":"string","enum":["user","course"],"description":"Only holds on this kind of subject."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminLegal-holdsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Place a legal hold","description":"Places a hold on a person or a course. A reason is required. A second open hold on the same subject answers 409 conflict. While a hold is open, erasure of the subject is refused.\n\nRequires scope `module.governance` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminLegal-holdsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject_type":{"type":"string","enum":["user","course"],"description":"user or course."},"subject_id":{"type":"string","format":"uuid","description":"The person or course."},"reason":{"type":"string","description":"Why (recorded in the audit log)."}},"required":["subject_type","subject_id","reason"]}}}}}},"/api/v1/admin/legal-holds/{id}":{"delete":{"summary":"Release a legal hold","description":"Releases an open hold. A reason is required, in the JSON body (`reason`) or the query string. Erasure of the subject is possible again once the hold is released.\n\nRequires scope `module.governance` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Hold id.","schema":{"type":"string","format":"uuid","description":"Hold id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminLegal-holdsIdItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such hold, or it is already released.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why the hold is being released."}},"required":["reason"]}}}}}},"/api/v1/admin/people":{"get":{"summary":"List people","description":"The organisation's active people with department, manager, audience type and (when present) the external id a partner system uses for them. A department-scoped administrator sees only people within their remit. Newest first; follow `meta.cursor.next`.\n\nRequires scope `module.org` and permission `learner.progress.tenant` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring of email or full name.","schema":{"type":"string","description":"Case-insensitive substring of email or full name."}},{"name":"department_id","in":"query","required":false,"description":"Only people whose department is this one.","schema":{"type":"string","format":"uuid","description":"Only people whose department is this one."}},{"name":"audience_type","in":"query","required":false,"description":"Only people of this audience type.","schema":{"type":"string","enum":["employee","contractor","customer","partner","public","buyer"],"description":"Only people of this audience type."}},{"name":"external_id","in":"query","required":false,"description":"Only the person with this external id.","schema":{"type":"string","description":"Only the person with this external id."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminPeopleItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/people/{id}":{"get":{"summary":"Get a person","description":"One person in this organisation with their profile fields, role grants and external id.\n\nRequires scope `module.org` and permission `learner.progress.tenant` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"User id.","schema":{"type":"string","format":"uuid","description":"User id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminPeopleIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such person in this organisation, or outside your delegated scope.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update a person","description":"Edit a person's department, title, manager and profile fields, and activate or deactivate them with `active`. Deactivation never deletes data. A delegated administrator can only change people within their own remit.\n\nRequires scope `module.org` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"User id.","schema":{"type":"string","format":"uuid","description":"User id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminPeopleIdItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`active` needs user.deactivate; the database also refuses deactivating yourself or the last administrator.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such person, or outside your delegated scope.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"department_id":{"type":["string","null"],"format":"uuid","description":"Department (null clears it)."},"title":{"type":"string","description":"Free-text job title."},"manager_id":{"type":["string","null"],"format":"uuid","description":"Line manager (null clears it). Refused if it would create a cycle."},"active":{"type":"boolean","description":"false deactivates the person, true reactivates them. Needs user.deactivate. Never deletes."},"full_name":{"type":"string","description":"Display name."},"job_title_id":{"type":["string","null"],"format":"uuid","description":"Job title record."},"location_id":{"type":["string","null"],"format":"uuid","description":"Office (a department of type location)."},"timezone":{"type":["string","null"],"description":"IANA time zone."},"locale":{"type":["string","null"],"description":"Language code."},"bio":{"type":["string","null"],"description":"Short biography."},"expertise":{"type":"array","description":"Up to 20 strings of at most 60 characters."}},"required":["title","active","full_name","expertise"]}}}}}},"/api/v1/admin/report-schedules":{"get":{"summary":"List report schedules","description":"Scheduled report deliveries, newest first.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminReport-schedulesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Schedule a report","description":"Creates a standing delivery of a report to up to 25 recipients. Needs tenant.admin.\n\nRequires scope `module.insights` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminReport-schedulesItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Schedule name."},"report_request":{"type":"object","description":"The report: { subject, columns?, filters?, sort?, limit? }."},"report_key":{"type":"string","description":"Alternative to report_request.subject."},"params":{"type":"object","description":"With report_key: { columns?, filters?, sort?, limit? }."},"format":{"type":"string","enum":["csv","pdf"],"description":"csv or pdf."},"cadence":{"type":"string","enum":["daily","weekly","monthly"],"description":"daily, weekly or monthly."},"send_hour_utc":{"type":"integer","description":"Hour (UTC), default 8."},"weekday":{"type":"integer","description":"Required for weekly."},"day_of_month":{"type":"integer","description":"Required for monthly (1 to 28)."},"recipients":{"type":"array","description":"Email addresses."},"is_active":{"type":"boolean","description":"Default true."},"include_analysis":{"type":"boolean","description":"Add an AI narrative above the attachment."}},"required":["name","report_request","report_key","params","format","cadence","send_hour_utc","weekday","day_of_month","recipients","is_active","include_analysis"]}}}}}},"/api/v1/admin/report-schedules/{id}":{"delete":{"summary":"Delete a report schedule","description":"Deletes the schedule and its delivery history. Needs tenant.admin.\n\nRequires scope `module.insights` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Schedule id.","schema":{"type":"string","format":"uuid","description":"Schedule id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminReport-schedulesIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get a report schedule","description":"One schedule with its delivery history.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Schedule id.","schema":{"type":"string","format":"uuid","description":"Schedule id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminReport-schedulesIdGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update a report schedule","description":"Change any field; the merged schedule is re-validated. Needs tenant.admin.\n\nRequires scope `module.insights` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Schedule id.","schema":{"type":"string","format":"uuid","description":"Schedule id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminReport-schedulesIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Schedule name."},"report_request":{"type":"object","description":"The report."},"format":{"type":"string","enum":["csv","pdf"],"description":"csv or pdf."},"cadence":{"type":"string","enum":["daily","weekly","monthly"],"description":"Cadence."},"send_hour_utc":{"type":"integer","description":"Hour (UTC)."},"weekday":{"type":"integer","description":"Weekly."},"day_of_month":{"type":"integer","description":"Monthly."},"recipients":{"type":"array","description":"Email addresses."},"is_active":{"type":"boolean","description":"Run or pause."},"include_analysis":{"type":"boolean","description":"AI narrative on or off."}},"required":["name","report_request","format","cadence","send_hour_utc","weekday","day_of_month","recipients","is_active","include_analysis"]}}}}}},"/api/v1/admin/reports":{"get":{"summary":"List reports","description":"The catalogue of report subjects (use `key` as `report_key` in POST /reports/run) and the organisation's saved report definitions.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminReportsItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/reports/run":{"post":{"summary":"Run a report","description":"Runs a catalogue report and returns rows (JSON, one bounded page) or, with `?format=csv`, a CSV of the whole result up to the row cap. Same engine and numbers as the Reports page.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"format","in":"query","required":false,"description":"json (default) or csv.","schema":{"type":"string","enum":["json","csv"],"description":"json (default) or csv."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminReportsRunItem"}},"required":["data"]}}}},"400":{"description":"Unknown subject, column, filter or operator.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"report_key":{"type":"string","description":"A subject key from GET /reports."},"params":{"type":"object","description":"{ columns?, filters?: [{ column, op, value? }], sort?: { column, ascending? }, limit?, offset? }."}},"required":["report_key","params"]}}}}}},"/api/v1/admin/retention-policies":{"get":{"summary":"List retention policies","description":"One row per retention class (audit log, learner progress, quiz attempts, certificates, AI usage, telemetry) with the configured window in days and the platform default. Read-only.\n\nRequires scope `module.governance` and permission `audit.view` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminRetention-policiesItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/admin/role-assignments":{"delete":{"summary":"Revoke a role","description":"Revokes one grant. Send `user_id`, `role_key` and (for a scoped grant) `scope_department_id` in the body or the query string. The last active administrator cannot be revoked.\n\nRequires scope `module.org` and permission `user.role.assign` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminRole-assignmentsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such grant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Person."},"role_key":{"type":"string","description":"Role key."},"scope_department_id":{"type":["string","null"],"format":"uuid","description":"Department scope (null = the whole organisation, which needs unrestricted scope yourself)."}},"required":["user_id","role_key"]}}}}},"get":{"summary":"List role assignments","description":"Role grants visible to the key's principal (a delegated administrator sees grants within their remit). Filter by person, role or department scope.\n\nRequires scope `module.org` and permission `user.role.assign` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"user_id","in":"query","required":false,"description":"Only this person's grants.","schema":{"type":"string","format":"uuid","description":"Only this person's grants."}},{"name":"role_key","in":"query","required":false,"description":"Only grants of this role.","schema":{"type":"string","description":"Only grants of this role."}},{"name":"scope_department_id","in":"query","required":false,"description":"Only grants scoped to this department.","schema":{"type":"string","format":"uuid","description":"Only grants scoped to this department."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AdminRole-assignmentsGetItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Assign a role","description":"Grants a role to a person, optionally scoped to a department. A role carrying `tenant.admin` is refused with 403 for every key. The database refuses escalation (a role with permissions you do not hold, a scope outside your remit, your own grants).\n\nRequires scope `module.org` and permission `user.role.assign` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminRole-assignmentsPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"Assigning would add a person beyond the plan's member limit (body carries `limit`, `value`, `used`, `upgrade_to`).","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Tenant-admin roles cannot be assigned through the API, or the assignment is outside your delegated scope.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Person."},"role_key":{"type":"string","description":"Role key."},"scope_department_id":{"type":["string","null"],"format":"uuid","description":"Department scope (null = the whole organisation, which needs unrestricted scope yourself)."}},"required":["user_id","role_key"]}}}}}},"/api/v1/admin/roles":{"get":{"summary":"List roles","description":"Tenant-defined roles with their permission keys and the number of people holding each (within your delegated remit). Read-only.\n\nRequires scope `module.org` and permission `user.role.assign` (see \"Auth\" in the module document's description).","tags":["admin"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdminRolesItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/ai/coach":{"post":{"summary":"Ask the AI coach","description":"One coach reply for the learner in X-Learner-Id. Send the conversation so far in `messages` (the last 30 turns are used; the last must be the learner's). Ground it with `lesson_id` (read as the learner) or your own `lesson_title` and `context`. Same quota, guardrails, credits and audit as the signed-in product. Supports `Idempotency-Key`.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiCoachItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"The lesson is not visible to this learner.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The locked guardrails could not be loaded; the call was refused.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","description":"Conversation turns: `[{ role: \"user\" | \"assistant\", content: string }]`."},"lesson_id":{"type":["string","null"],"format":"uuid","description":"A lesson the learner can see; its text grounds the answer."},"lesson_title":{"type":["string","null"],"description":"Title to use when `lesson_id` is not sent."},"context":{"type":["string","null"],"description":"Lesson text to ground the answer when `lesson_id` is not sent (cut to 12000 characters). Treated as untrusted data."}},"required":["messages"]}}}}}},"/api/v1/ai/flashcards":{"post":{"summary":"Generate flashcards","description":"Generates 5-10 review flashcards for the learner in X-Learner-Id from `lesson_id` (read as the learner) or from your own `text`. The activity is registered under the learn module, so the organisation must be entitled to it as well as to AI. Nothing is stored; your product keeps the cards. Supports `Idempotency-Key`.\n\nRequires scope `module.learn` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiFlashcardsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"The lesson is not visible to this learner.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The model did not return usable flashcards.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lesson_id":{"type":["string","null"],"format":"uuid","description":"A lesson the learner can see. Use this or `text`."},"text":{"type":["string","null"],"description":"Source text (cut to 16000 characters). Treated as untrusted data. Use this or `lesson_id`."},"title":{"type":["string","null"],"description":"Title for the material when `text` is sent."}},"required":[]}}}}}},"/api/v1/ai/grade":{"post":{"summary":"Draft AI rubric scores","description":"Asks the model to score a submission against a rubric and stores the result as an AI suggestion per criterion. It never sets the human score or the final grade. Runs as the person the key acts as (who must hold `ai.use`; the key needs `assessment.grade`). Supports `Idempotency-Key`.\n\nRequires scope `module.assessment` and permission `assessment.grade` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiGradeItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The model did not return scores.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"response_id":{"type":"string","format":"uuid","description":"The assessment response being graded."},"rubric_id":{"type":"string","format":"uuid","description":"The rubric to score against."},"submission_text":{"type":"string","description":"The submission text (cut to 8000 characters). Treated as untrusted data."}},"required":["response_id","rubric_id","submission_text"]}}}}}},"/api/v1/ai/mentor-checkin":{"post":{"summary":"Mentor check-in","description":"A short, personal check-in grounded in the learner's recent practice. Send `conversation_id` from a previous reply to continue the thread. Supports `Idempotency-Key`.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiMentor-checkinItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"What the learner says (cut to 2000 characters)."},"learner_goals":{"type":["string","null"],"description":"Free-text goals (cut to 1000 characters). Treated as untrusted data; not stored."},"conversation_id":{"type":["string","null"],"format":"uuid","description":"Continue this conversation (from a previous reply)."}},"required":["message"]}}}}}},"/api/v1/ai/plan":{"post":{"summary":"Generate a learning plan","description":"Builds and saves a personal learning plan for the learner in X-Learner-Id from their competency gaps, a time budget and the catalogue. The model can only choose among courses the engine offered. Supports `Idempotency-Key`.\n\nRequires scope `module.paths` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiPlanItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"weekly_minutes":{"type":["integer","null"],"description":"Time the learner can give each week (default 180); sets how many courses the plan holds (3-8)."},"reason":{"type":["string","null"],"description":"Free text shown back in the plan's description."},"target_job_title_id":{"type":["string","null"],"format":"uuid","description":"Plan towards this job title instead of the learner's current one."}},"required":[]}}}}}},"/api/v1/ai/questions/generate":{"post":{"summary":"Generate draft questions","description":"Generates 1-10 draft question-bank items on a topic. Items are saved as drafts (never approved) and returned without answer keys. Runs as the person the key acts as, who must hold `ai.use` and the key `assessment.author`. Supports `Idempotency-Key`.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiQuestionsGenerateItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The model did not return usable items.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"topic":{"type":"string","description":"What the questions are about (cut to 1000 characters)."},"format":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Question format."},"count":{"type":["integer","null"],"description":"How many (1-10, default 1)."},"lesson_context":{"type":["string","null"],"description":"Source text to ground the questions (cut to 4000 characters). Treated as untrusted data."},"course_id":{"type":["string","null"],"format":"uuid","description":"Attribute the drafts to this course."}},"required":["topic","format"]}}}}}},"/api/v1/ai/reading-companion":{"post":{"summary":"Ask the reading companion","description":"Summarises, explains or simplifies a lesson for the learner in X-Learner-Id. Send `question` (one turn) or `messages` (the conversation so far, ending with the learner). Ground it with `lesson_id` (read as the learner) or your own `lesson_title` and `context`. Supports `Idempotency-Key`.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiReading-companionItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"The lesson is not visible to this learner.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":["string","null"],"description":"The learner's question. Use this or `messages`."},"messages":{"type":["array","null"],"description":"Conversation turns `[{ role, content }]`, ending with the learner. Use this or `question`."},"lesson_id":{"type":["string","null"],"format":"uuid","description":"A lesson the learner can see; its text is the reading material."},"lesson_title":{"type":["string","null"],"description":"Title to use when `lesson_id` is not sent."},"context":{"type":["string","null"],"description":"The text to read when `lesson_id` is not sent (cut to 16000 characters). Treated as untrusted data."},"reading_level":{"type":["string","null"],"enum":["simple","standard","advanced"],"description":"simple, standard (default) or advanced."}},"required":[]}}}}}},"/api/v1/ai/roleplay/sessions":{"post":{"summary":"Start a roleplay session","description":"Starts a roleplay for the learner named in X-Learner-Id and returns the persona's opening line. Give either `scenario_id` (`<lesson_id>:<block_id>` of a roleplay block the learner can see) or an inline `scenario`. Same quota, guardrails, credits and audit as the signed-in product; the usage row records the API key. Supports `Idempotency-Key`: a retry replays the stored response and charges nothing.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiRoleplaySessionsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance that pays for this organisation cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"The scenario_id does not name a roleplay block this learner can see.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The Idempotency-Key was used for a different request.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance for this learner or organisation is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The locked guardrails could not be loaded; the call was refused.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scenario_id":{"type":["string","null"],"description":"`<lesson_id>:<block_id>` of a roleplay block in a lesson the learner can see. Omit when sending `scenario`."},"scenario":{"type":["object","null"],"description":"Inline scenario: `{ scenario: string, persona: string | {name, role, stance}, objectives: string[], title?: string }`. Omit when sending `scenario_id`."}},"required":[]}}}}}},"/api/v1/ai/roleplay/sessions/{id}/score":{"post":{"summary":"Score a roleplay session","description":"Scores the whole transcript against the scenario's objectives, closes the session and emits the `roleplay.scored` webhook. A second call returns the stored result without calling the model. Needs at least one exchange. Supports `Idempotency-Key`.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The session_id from POST /ai/roleplay/sessions.","schema":{"type":"string","format":"uuid","description":"The session_id from POST /ai/roleplay/sessions."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiRoleplaySessionsIdScoreItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such session for this learner.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"No exchange to score yet.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/ai/roleplay/sessions/{id}/turns":{"post":{"summary":"Send a roleplay turn","description":"Sends the learner's message and returns the persona's reply. 409 `conflict` when the session is finished or has reached its 40-exchange limit. Supports `Idempotency-Key`.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The session_id from POST /ai/roleplay/sessions.","schema":{"type":"string","format":"uuid","description":"The session_id from POST /ai/roleplay/sessions."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiRoleplaySessionsIdTurnsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such session for this learner.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The session is finished or at its turn limit.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"What the learner says (cut to 4000 characters)."}},"required":["message"]}}}}}},"/api/v1/ai/translate":{"post":{"summary":"Translate a lesson (draft)","description":"Generates a translation candidate for one lesson into `locale` (a BCP 47 tag). The locale is added to the course's target locales if it is not already one. The candidate is reviewed and applied in the Studio; nothing a learner sees changes. Runs as the person the key acts as. Supports `Idempotency-Key`.\n\nRequires scope `module.authoring` and permission `lesson.edit` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AiTranslateItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"The course or lesson is not visible to the acting user.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The translation changed the lesson's block ids or was malformed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"The course the lesson belongs to."},"lesson_id":{"type":"string","format":"uuid","description":"The lesson to translate."},"locale":{"type":"string","description":"Target language, a BCP 47 tag such as `es` or `pt-BR`."}},"required":["course_id","lesson_id","locale"]}}}}}},"/api/v1/assess/assessments":{"get":{"summary":"List assessments","description":"With `X-Learner-Id`: the published assessments this learner can take, with their attempts used and remaining. Without it (the key needs `assessment.author`): every assessment in the organisation, any status. Cursor-paginated, newest first.\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Filter to one course.","schema":{"type":"string","format":"uuid","description":"Filter to one course."}},{"name":"status","in":"query","required":false,"description":"Administrator variant only: draft, published or archived.","schema":{"type":"string","description":"Administrator variant only: draft, published or archived."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, title. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","title","-created_at","-title"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssessAssessmentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/assessments/{id}":{"get":{"summary":"Get one assessment","description":"The assessment's rules and, for the learner named in `X-Learner-Id`, their attempts used and remaining. No questions and no answer keys. Without the header (key needs `assessment.author`) the administrator view, which also reports `question_count`.\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Assessment id.","schema":{"type":"string","format":"uuid","description":"Assessment id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAssessmentsIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/attempts":{"get":{"summary":"List quiz attempts","description":"Quiz attempts across the caller's tenant, from the append-only attempts ledger. Never returns a quiz block's correct answer — this table doesn't carry one.\n\nRequires scope `module.assessment` and permission `learner.progress.tenant` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"lesson_id","in":"query","required":false,"description":"Filter to one lesson.","schema":{"type":"string","format":"uuid","description":"Filter to one lesson."}},{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"correct","in":"query","required":false,"description":"Filter to correct (true) or incorrect (false) attempts.","schema":{"type":"boolean","description":"Filter to correct (true) or incorrect (false) attempts."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: answered_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["answered_at","-answered_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssessAttemptsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Start or resume an attempt","description":"Acts as the learner in `X-Learner-Id`. Returns the attempt id, its number, the question set (no answer keys) and, for a timed assessment, `deadline`. An open attempt is resumed rather than replaced. A proctored assessment answers `409 proctored_requires_top_level` with a hint to open it top-level through `POST /api/v1/embed/sessions`.\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAttemptsPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The assessment is proctored (open it top-level through an embed session), or `conflict` with reason `attempts_exhausted` when the attempt limit is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assessment_id":{"type":"string","format":"uuid","description":"The assessment to attempt. It must be published."}},"required":["assessment_id"]}}}}}},"/api/v1/assess/attempts/{id}/grade":{"post":{"summary":"Grade an attempt (human, rubric)","description":"Body `{ grades: [{ response_id, rubric_id, scores: [{ criterion_key, score, rationale? }] }], feedback?, finalize? }`. Every `response_id` must belong to this attempt, which must be `submitted`. `feedback` is used as the rationale of any criterion sent without one. With `finalize` true (the default) the attempt is finalized afterwards: `graded` once every response has a grade, otherwise it stays `submitted` and `awaiting_human_grading` says how many remain. The grade is recorded against the person the key acts as, who must also hold `lesson.edit`.\n\nRequires scope `module.assessment` and permission `assessment.grade` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Attempt id.","schema":{"type":"string","format":"uuid","description":"Attempt id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAttemptsIdGradeItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"grades":{"type":"array","description":"One entry per response being graded: { response_id, rubric_id, scores: [{ criterion_key, score, rationale? }] }."},"feedback":{"type":["string","null"],"description":"Default rationale for criteria sent without one."},"finalize":{"type":["boolean","null"],"description":"Finalize the attempt after grading. Default true."}},"required":["grades"]}}}}}},"/api/v1/assess/attempts/{id}/responses":{"put":{"summary":"Save one answer","description":"Acts as the learner in `X-Learner-Id`, who must own the attempt. Idempotent per question: the latest answer replaces the earlier one. The response never says whether the answer was right. Refused with 409 `conflict` when the attempt is closed (`attempt_closed`) or past its deadline (`time_expired`).\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Attempt id.","schema":{"type":"string","format":"uuid","description":"Attempt id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAttemptsIdResponsesItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question_id":{"type":"string","format":"uuid","description":"A question of this attempt's assessment."},"response":{"type":["object","null"],"description":"The answer, in the shape the question's format calls for (choice id, text, matching pairs, ordering, uploaded file path). The UI calls this field `answer`; both names are accepted."}},"required":["question_id"]}}}}}},"/api/v1/assess/attempts/{id}/result":{"get":{"summary":"Get an attempt result","description":"Acts as the learner in `X-Learner-Id`, who must own the attempt. 409 `conflict` with reason `not_finalized` while the attempt is still in progress.\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Attempt id.","schema":{"type":"string","format":"uuid","description":"Attempt id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAttemptsIdResultItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/attempts/{id}/submit":{"post":{"summary":"Submit an attempt","description":"Acts as the learner in `X-Learner-Id`, who must own the attempt. Returns the result as the learner sees it: status, score, pass mark, passed, a summary, and (only when the assessment allows answer review and the attempt is finished) a per-question review. `status` is `graded` or, when a human must grade, `submitted` with `awaiting_human_grading`. Submitting an attempt that is already closed returns its result again and emits no event.\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Attempt id.","schema":{"type":"string","format":"uuid","description":"Attempt id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAttemptsIdSubmitItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/attempts/{id}/upload-url":{"post":{"summary":"Sign a file-upload answer","description":"Acts as the learner in `X-Learner-Id`, who must own the attempt. Accepted types: PDF, Office documents, JPEG/PNG/WebP, plain text, CSV, ZIP. Maximum 10 MB. Upload the bytes with `PUT upload_url`, then submit `path` as the answer to the file-upload question.\n\nRequires scope `module.assessment` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Attempt id.","schema":{"type":"string","format":"uuid","description":"Attempt id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessAttemptsIdUpload-urlItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"413":{"description":"The file is larger than 10 MB.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"file_name":{"type":"string","description":"The file's name, used for its extension only."},"content_type":{"type":"string","description":"The MIME type of the file."},"size":{"type":"integer","description":"The size in bytes."}},"required":["file_name","content_type","size"]}}}}}},"/api/v1/assess/certificates":{"get":{"summary":"List issued certificates","description":"Certificates issued across the caller's tenant. Deliberately omits verification_code — see this route's file header.\n\nRequires scope `module.assessment` and permission `learner.progress.tenant` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Filter to one course.","schema":{"type":"string","format":"uuid","description":"Filter to one course."}},{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: issued_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["issued_at","-issued_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssessCertificatesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/certificates/{id}/pdf":{"get":{"summary":"Get a certificate's printable link","description":"Returns `print_url` (the printable, branded certificate page) and `verify_url` (public JSON verification) for a certificate in this organisation. There is no server-side PDF: `format` is `html`. A revoked certificate answers 409 `conflict`.\n\nRequires scope `module.assessment` and permission `certificate.issue` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Certificate id, from POST /assess/certificates/issue or GET /assess/certificates.","schema":{"type":"string","format":"uuid","description":"Certificate id, from POST /assess/certificates/issue or GET /assess/certificates."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessCertificatesIdPdfItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such certificate in this organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The certificate was revoked.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/certificates/issue":{"post":{"summary":"Issue a certificate","description":"Issues the certificate for the learner in X-Learner-Id and `course_id`. 409 `completion_required` unless the learner has completed the course. `display_name` is printed on the certificate and is not stored on the learner's profile; omit it to print the profile name. Issuing again returns the same certificate unchanged (`created: false`); a revoked certificate answers 409 `conflict`. There is no PDF generator: a certificate is a printable HTML page, so the response carries `verify_url` (public JSON check) and `print_url` (the printable page). Supports `Idempotency-Key`.\n\nRequires scope `module.assessment` and permission `certificate.issue` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessCertificatesIssueItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such learner in this organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The certificate was revoked.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"The completed course."},"display_name":{"type":["string","null"],"description":"The name to print (max 200 characters)."}},"required":["course_id"]}}}}}},"/api/v1/assess/completion-records":{"get":{"summary":"List training completion records","description":"OSHA-style training records: one immutable, snapshotted row per course completion, for the tenant.\n\nRequires scope `module.assessment` and permission `learner.progress.tenant` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Filter to completions originally taken on this course.","schema":{"type":"string","format":"uuid","description":"Filter to completions originally taken on this course."}},{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: completed_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["completed_at","-completed_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssessCompletion-recordsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/grading-queue":{"get":{"summary":"List attempts awaiting grading","description":"Submitted attempts with essay or file-upload responses that need a human grade, oldest first. Each response says whether it still `needs_grading` and, for a rubric-graded question, which `rubric_id` to grade it against. Grade with `POST /api/v1/assess/attempts/{id}/grade`.\n\nRequires scope `module.assessment` and permission `assessment.grade` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"assessment_id","in":"query","required":false,"description":"Filter to one assessment.","schema":{"type":"string","format":"uuid","description":"Filter to one assessment."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: submitted_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["submitted_at","-submitted_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssessGrading-queueItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/question-bank":{"get":{"summary":"List question-bank items","description":"Questions in the organisation's bank, newest first. Never returns answer keys.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"tag","in":"query","required":false,"description":"Only questions carrying this tag.","schema":{"type":"string","description":"Only questions carrying this tag."}},{"name":"format","in":"query","required":false,"description":"Only this question format.","schema":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Only this question format."}},{"name":"status","in":"query","required":false,"description":"draft, approved or archived.","schema":{"type":"string","description":"draft, approved or archived."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","-created_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssessQuestion-bankItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Create a question","description":"Creates a human-authored question, approved on creation. `answer_key` is optional, write-only, and ignored for essay and file_upload questions; its shape depends on the format (an option index for multiple choice, a list of accepted strings for short answer, pairs for matching, an order for ordering). If the question is saved but its key is not, the response is still 201 and `meta.warning` says so; retry with PUT .../answer-key.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Question format."},"prompt":{"type":"string","description":"The question text."},"tags":{"type":["array","null"],"description":"Tags."},"difficulty":{"type":["string","null"],"enum":["easy","medium","hard"],"description":"Default medium."},"points":{"type":["number","null"],"description":"Default 1."},"config":{"type":["object","null"],"description":"Answer-free, format-specific configuration."},"explanation":{"type":["string","null"],"description":"Shown after the attempt."},"answer_key":{"type":["object","null"],"description":"The correct answer. Write-only."}},"required":["format","prompt"]}}}}}},"/api/v1/assess/question-bank/{id}":{"delete":{"summary":"Delete a question","description":"Deletes the question and its answer key. Refused (400) while an assessment still draws on it.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Question id.","schema":{"type":"string","format":"uuid","description":"Question id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get a question","description":"One question-bank item, without its answer key.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Question id.","schema":{"type":"string","format":"uuid","description":"Question id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankIdGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Edit, approve or archive a question","description":"Send any of `prompt`, `tags`, `difficulty`, `points`, `config`, `explanation`, `status`. `status` may only become `approved` (the approver is the person the key acts as) or `archived`; it can never be set back to `draft`. Change the key with `PUT .../answer-key`.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Question id.","schema":{"type":"string","format":"uuid","description":"Question id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":["string","null"],"description":"The question text."},"tags":{"type":["array","null"],"description":"Tags."},"difficulty":{"type":["string","null"],"enum":["easy","medium","hard"],"description":"Difficulty."},"points":{"type":["number","null"],"description":"Points."},"config":{"type":["object","null"],"description":"Answer-free configuration."},"explanation":{"type":["string","null"],"description":"Review explanation."},"status":{"type":["string","null"],"enum":["approved","archived"],"description":"approved or archived."}},"required":[]}}}}}},"/api/v1/assess/question-bank/{id}/answer-key":{"get":{"summary":"Read a question's answer key","description":"The correct answer for one question, or `key: null` when none is set (essay and file_upload questions have none). Authoring keys only; every read is audited.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Question id.","schema":{"type":"string","format":"uuid","description":"Question id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankIdAnswer-keyItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Set a question's answer key","description":"Replaces the key. Write-only: the response never echoes it.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Question id.","schema":{"type":"string","format":"uuid","description":"Question id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankIdAnswer-keyPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"object","description":"The correct answer, in the shape of the question's format."}},"required":["key"]}}}}}},"/api/v1/assess/question-bank/generate":{"post":{"summary":"Generate draft questions (AI)","description":"Generates 1 to 10 questions on a topic and saves them as drafts. The key needs `assessment.author` and the person it acts as needs `ai.use`; the organisation's AI opt-out, quota and credits apply exactly as in the admin UI (402 `insufficient_credits`, 429 `ai_quota_exceeded`, 403 `feature_disabled`). Answer keys are stored, never returned.\n\nRequires scope `module.assessment` and permission `assessment.author` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessQuestion-bankGenerateItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"topic":{"type":"string","description":"What the questions are about."},"format":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Question format."},"count":{"type":["integer","null"],"description":"1 to 10. Default 1."},"lesson_context":{"type":["string","null"],"description":"Optional source text to ground the questions (first 4000 characters used)."}},"required":["topic","format"]}}}}}},"/api/v1/assess/reaction-forms/{id}":{"get":{"summary":"Get a reaction form","description":"The form's questions (rating, nps, text, multiple_choice). Acts as the learner in `X-Learner-Id`. Needs a key scoped `module.insights`.\n\nRequires scope `module.insights` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Reaction form id.","schema":{"type":"string","format":"uuid","description":"Reaction form id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessReaction-formsIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/assess/reaction-forms/{id}/submit":{"post":{"summary":"Submit a reaction-form response","description":"Body `{ answers: { <question id>: <answer> } }`. Acts as the learner in `X-Learner-Id`. 404 when the form does not exist, is inactive, or is not in the organisation. 400 when a required question is unanswered; 409 `already_submitted` when a non-anonymous form already has a response from this learner (anonymous forms are not limited).\n\nRequires scope `module.insights` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["assess"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Reaction form id.","schema":{"type":"string","format":"uuid","description":"Reaction form id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssessReaction-formsIdSubmitItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"answers":{"type":"object","description":"Answers keyed by the form's question ids."}},"required":["answers"]}}}}}},"/api/v1/authoring/courses":{"post":{"summary":"Build a course (Studio pipeline)","description":"Creates a draft course from a title, a brief and optional sources, and starts the Studio build: blueprint, automatic approval, modules and lessons, lesson drafts. Returns 202 with `course_id` and `job_id`; poll GET /authoring/jobs/{job_id}. The key needs `course.create`, `lesson.edit` and `lesson.review` (the build approves its own blueprint), and the person it acts as must hold them. Sources: `{ type: \"text\" | \"url\" | \"upload\", value }` (a url is fetched by the platform, SSRF-guarded; an upload is an `upload_id` from POST /authoring/uploads). The AI runs through the gateway as the acting person, so their quota and the organisation's credits apply. Supports `Idempotency-Key`.\n\nRequires scope `module.authoring` and permission `course.create` (see \"Auth\" in the module document's description).","tags":["authoring"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AuthoringCoursesItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The key lacks `lesson.edit` or `lesson.review`, or the acting person may not create courses.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The Idempotency-Key was used for a different request.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"A source could not be read, or the brief was rejected.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The organisation's concurrent authoring-run cap is reached.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Course title (1-200 characters)."},"brief":{"type":"string","description":"What the course must achieve and for whom: the performance need (up to 3000 characters)."},"sources":{"type":["array","null"],"description":"Up to 10 sources: `[{ type: \"text\" | \"url\" | \"upload\", value: string, title?: string }]`."},"audience":{"type":["string","null"],"description":"The learners (up to 1000 characters)."},"duration_minutes":{"type":["integer","null"],"description":"Target e-learning length, 15-480 minutes (8 hours, the platform course-length limit; default 60)."},"locale":{"type":["string","null"],"description":"Source language, a BCP 47 tag (default `en`)."},"apply_drafts":{"type":["boolean","null"],"description":"Apply the generated lesson drafts to the lessons (default true). When false the job ends with the drafts waiting in the Studio."}},"required":["title","brief"]}}}}}},"/api/v1/authoring/courses/{id}/export":{"post":{"summary":"Export a course","description":"Builds an export of the course and returns a signed `download_url` valid for one hour. `format` is `scorm12` (SCORM 1.2 zip), `xapi` (xAPI zip), `html` (a single page) or `pdf`. `release_id` pins a SCORM or xAPI package to an immutable release; omit it to export the current draft. Needs the `course.export` permission on the key.\n\nRequires scope `module.authoring` and permission `course.export` (see \"Auth\" in the module document's description).","tags":["authoring"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The course.","schema":{"type":"string","format":"uuid","description":"The course."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AuthoringCoursesIdExportItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such course visible to the acting person.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"type":"string","enum":["scorm12","xapi","html","pdf"],"description":"Export format."},"release_id":{"type":["string","null"],"format":"uuid","description":"Pin a SCORM 1.2 or xAPI export to a release."},"launch":{"type":["string","null"],"enum":["popup","frame"],"description":"SCORM 1.2 / xAPI: how the LMS opens the course (default frame)."}},"required":["format"]}}}}}},"/api/v1/authoring/jobs/{id}":{"get":{"summary":"Get a course build job","description":"Status of a build started by POST /authoring/courses. `stage` is blueprint, structure, lessons, apply or done; `status` is running, succeeded or failed; `progress` is 0-100. On failure `error` carries a code and message, and the course keeps whatever was built so it can be finished in the Studio. Polling advances the job.\n\nRequires scope `module.authoring` and permission `course.create` (see \"Auth\" in the module document's description).","tags":["authoring"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The job_id from POST /authoring/courses.","schema":{"type":"string","format":"uuid","description":"The job_id from POST /authoring/courses."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AuthoringJobsIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such job in this organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/authoring/uploads":{"post":{"summary":"Get a signed upload URL for a source document","description":"Returns a signed `upload_url` for a PDF, DOC or DOCX (max 10 MB). PUT the raw file bytes to it with the `content_type` header. The URL is valid for 15 minutes (`expires_at`); the upload is then used as `{ \"type\": \"upload\", \"value\": upload_id }` in POST /authoring/courses within 24 hours. Not idempotent: each call creates a new upload.\n\nRequires scope `module.authoring` and permission `course.create` (see \"Auth\" in the module document's description).","tags":["authoring"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AuthoringUploadsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"file_name":{"type":"string","description":"The file name, e.g. `policy.pdf`."},"content_type":{"type":"string","enum":["application/pdf","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"],"description":"The file's media type."}},"required":["file_name","content_type"]}}}}}},"/api/v1/catalogue/items/{id}/add":{"post":{"summary":"Add a catalogue item to the organisation's library","description":"Creates a draft external course from a synced catalogue item (201). Idempotent: adding the same item again creates nothing and returns the existing course (200). `403 permission_denied` (`reason: no_provider_licence`) when the organisation has no licence for that provider.\n\nRequires scope `module.integrations` and permission `course.create` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The catalogue item id from the search endpoint.","schema":{"type":"string","format":"uuid","description":"The catalogue item id from the search endpoint."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CatalogueItemsIdAddItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/catalogue/search":{"get":{"summary":"Search the organisation's content catalogue","description":"Case-insensitive search over the title and description of the organisation's synced provider items. `licence_source` says whether the licence in use is the organisation's own (`tenant`) or inherited from its partner (`partner`). Page with `page_size` (default 25, max 100) and `meta.cursor.next`.\n\nRequires scope `module.integrations` and permission `course.create` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"q","in":"query","required":false,"description":"Search text. Omit to list.","schema":{"type":"string","description":"Search text. Omit to list."}},{"name":"provider_key","in":"query","required":false,"description":"Restrict to one provider.","schema":{"type":"string","description":"Restrict to one provider."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CatalogueSearchItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/commerce/cart":{"get":{"summary":"Read the learner's cart","description":"The learner's open cart and its line items. `cart` is null when there is no open cart.\n\nRequires scope `module.commerce` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["commerce"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CommerceCartItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Add a listing to the learner's cart","description":"Adds one of a listing to the learner's open cart, creating the cart if needed, and returns the cart as GET does. A cart holds one currency. There is no quantity: adding a listing already in the cart changes nothing, and `qty` other than 1 is refused.\n\nRequires scope `module.commerce` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["commerce"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CommerceCartPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listing_id":{"type":"string","format":"uuid","description":"The listing to add."},"qty":{"type":"integer","description":"Optional. Must be 1 when sent: a cart line is always one of the listing."}},"required":["listing_id","qty"]}}}}}},"/api/v1/commerce/checkout":{"post":{"summary":"Check out the learner's cart","description":"Creates a pending order from the learner's open cart and returns `checkout_url`, a hosted payment page on the organisation's own payment account. An optional `Idempotency-Key` makes a retry replay the first answer instead of failing on an already-closed cart. Refused 409 `payment_account_required` when the organisation has not connected a payment account.\n\nRequires scope `module.commerce` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["commerce"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CommerceCheckoutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The organisation has not connected a payment account. Nothing was created.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"502":{"description":"The organisation's payment provider refused to create the checkout. The order exists, pending.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["razorpay","stripe"],"description":"Which of the organisation's connected accounts to use. Defaults to its active account."}},"required":["provider"]}}}}}},"/api/v1/commerce/listings":{"get":{"summary":"List what the organisation sells","description":"The organisation's active store listings with price and access model. Send the learner in `X-Learner-Id`. Prices are integer minor units (paise, cents).\n\nRequires scope `module.commerce` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["commerce"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","-created_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommerceListingsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/commerce/orders":{"get":{"summary":"List the learner's orders","description":"The learner's orders, newest first. `status` is `pending` until the provider's verified webhook reports payment, then `paid`. Send the learner in `X-Learner-Id`.\n\nRequires scope `module.commerce` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["commerce"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: placed_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["placed_at","-placed_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommerceOrdersItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/commerce/orders/all":{"get":{"summary":"List all of the organisation's orders","description":"Every order placed in the organisation, newest first. Filter by `status` or `buyer_user_id`.\n\nRequires scope `module.commerce` and permission `tenant.admin` (see \"Auth\" in the module document's description).","tags":["commerce"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Filter by order status.","schema":{"type":"string","enum":["pending","paid","failed","cancelled","refunded"],"description":"Filter by order status."}},{"name":"buyer_user_id","in":"query","required":false,"description":"Filter to one buyer.","schema":{"type":"string","format":"uuid","description":"Filter to one buyer."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: placed_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["placed_at","-placed_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CommerceOrdersAllItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/development/enrolment-requests":{"get":{"summary":"List enrolment requests","description":"Course enrolment requests, newest first. Filter with `status`. At most `limit` rows (default 200, max 500); not cursor-paged.\n\nRequires scope `module.learn` and permission `course.assign` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"\"pending\", \"approved\", \"denied\" or \"withdrawn\".","schema":{"type":"string","enum":["pending","approved","denied","withdrawn"],"description":"\"pending\", \"approved\", \"denied\" or \"withdrawn\"."}},{"name":"limit","in":"query","required":false,"description":"Rows to return, 1 to 500.","schema":{"type":"integer","description":"Rows to return, 1 to 500."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DevelopmentEnrolment-requestsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/development/enrolment-requests/{id}/approve":{"post":{"summary":"Approve an enrolment request","description":"Approves a pending request. The learner is enrolled in the course in the same transaction and notified. 409 when it was already decided; 404 when it does not exist in this organisation.\n\nRequires scope `module.learn` and permission `course.assign` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Enrolment request id.","schema":{"type":"string","format":"uuid","description":"Enrolment request id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentEnrolment-requestsIdApproveItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The request was already decided.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":["string","null"],"description":"Decision note shown to the learner, up to 1000 characters."}},"required":[]}}}}}},"/api/v1/development/enrolment-requests/{id}/deny":{"post":{"summary":"Deny an enrolment request","description":"Denies a pending request with an optional note; the learner is notified. 409 when it was already decided; 404 when it does not exist in this organisation.\n\nRequires scope `module.learn` and permission `course.assign` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Enrolment request id.","schema":{"type":"string","format":"uuid","description":"Enrolment request id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentEnrolment-requestsIdDenyItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The request was already decided.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":["string","null"],"description":"Decision note shown to the learner, up to 1000 characters."}},"required":[]}}}}}},"/api/v1/development/learning-requests":{"get":{"summary":"List learning requests","description":"The organisation's learning-request backlog as the learner sees it.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DevelopmentLearning-requestsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Submit a learning request","description":"Creates a request in the learner's name. `title` is 3 to 160 characters.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"201":{"description":"The resource was created.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentLearning-requestsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"What training is needed."},"description":{"type":["string","null"],"description":"Detail."},"skill_id":{"type":["string","null"],"format":"uuid","description":"Related skill."}},"required":["title"]}}}}}},"/api/v1/development/learning-requests/{id}":{"patch":{"summary":"Edit a learning request","description":"Changes title, description or skill_id of the learner's own open request.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Learning request id.","schema":{"type":"string","format":"uuid","description":"Learning request id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentLearning-requestsIdItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":["string","null"],"description":"3 to 160 characters."},"description":{"type":["string","null"],"description":"Detail."},"skill_id":{"type":["string","null"],"format":"uuid","description":"Related skill, or null to clear."}},"required":[]}}}}}},"/api/v1/development/learning-requests/{id}/vote":{"post":{"summary":"Toggle an upvote","description":"Adds the learner's upvote to a learning request, or removes it if already given.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Learning request id.","schema":{"type":"string","format":"uuid","description":"Learning request id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentLearning-requestsIdVoteItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/development/observations":{"get":{"summary":"List manager observations","description":"The observation queue. `scope=team` (default) is the queue of the person the key acts as; `scope=tenant` lists the whole organisation and needs a tenant-wide progress permission on that person.\n\nRequires scope `module.insights` and permission `learner.progress.team` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"scope","in":"query","required":false,"description":"\"team\" (default) or \"tenant\".","schema":{"type":"string","enum":["team","tenant"],"description":"\"team\" (default) or \"tenant\"."}},{"name":"status","in":"query","required":false,"description":"\"requested\" or \"submitted\".","schema":{"type":"string","description":"\"requested\" or \"submitted\"."}},{"name":"learner_id","in":"query","required":false,"description":"Only observations of this learner.","schema":{"type":"string","format":"uuid","description":"Only observations of this learner."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: requested_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["requested_at","-requested_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DevelopmentObservationsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Request an observation of a learner","description":"Creates a `requested` observation for a learner, addressed to that learner's manager. Name the learner with `learner` (external id or user id). The person the key acts as must manage the learner or hold `learner.progress.tenant`.\n\nRequires scope `module.insights` and permission `learner.progress.team` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"201":{"description":"The resource was created.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentObservationsPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"learner":{"type":"string","description":"The learner's external id or user id."},"competency_id":{"type":["string","null"],"format":"uuid","description":"Competency to verify."},"notes":{"type":["string","null"],"description":"What to observe, up to 2000 characters."}},"required":["learner"]}}}}}},"/api/v1/development/observations/{id}/record":{"post":{"summary":"Record an observation outcome","description":"Submits the outcome (did behaviour change, a 1 to 5 rating, optional notes). Must be the assigned manager, and the observation must still be `requested`.\n\nRequires scope `module.insights` and permission `learner.progress.team` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Observation id.","schema":{"type":"string","format":"uuid","description":"Observation id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentObservationsIdRecordItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"behavior_changed":{"type":"boolean","description":"Whether the learner's behaviour changed."},"rating":{"type":"integer","description":"Rating, 1 to 5."},"notes":{"type":["string","null"],"description":"Optional notes."}},"required":["behavior_changed","rating"]}}}}}},"/api/v1/development/observations/request":{"post":{"summary":"Ask the learner's manager for an observation","description":"Creates a request for the learner's manager to observe and verify the learner (optionally on one competency). One open request per competency per learner.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"201":{"description":"The resource was created.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentObservationsRequestItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The learner has no manager on record, or already has an open request for this competency.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"competency_id":{"type":["string","null"],"format":"uuid","description":"Competency to be verified. Omit for a general request."},"notes":{"type":["string","null"],"description":"What the learner wants observed, up to 2000 characters."}},"required":[]}}}}}},"/api/v1/development/plans":{"get":{"summary":"The learner's personal learning plan","description":"The learner's personal plan with its steps and progress, or `plan: null` when none exists yet. Create or refresh it with POST /api/v1/development/plans/generate.\n\nRequires scope `module.paths` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentPlansItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/development/plans/generate":{"post":{"summary":"Generate or refresh the learner's plan","description":"Builds a personal learning plan from the learner's skill gaps, their weekly time budget and the recommendation ranking, with AI selection through the platform's AI gateway. Uses AI credits of the organisation. Replaces the learner's existing personal plan.\n\nRequires scope `module.paths` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["development"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DevelopmentPlansGenerateItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The organisation has no AI credits left (gateway code, not in the shared catalogue).","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The plan could not be built, e.g. no published courses to choose from.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"weekly_minutes":{"type":["integer","null"],"description":"Minutes the learner can give each week. Default 180; shapes how many courses (3 to 8)."},"reason":{"type":["string","null"],"description":"Free text shown back in the plan description, e.g. why it is being regenerated."},"target_job_title_id":{"type":["string","null"],"format":"uuid","description":"Plan towards this job title instead of the learner's own."}},"required":[]}}}}}},"/api/v1/embed/sessions":{"post":{"summary":"Mint an embed session","description":"Returns a one-time URL (valid for 60 seconds) that signs the learner in and opens `target` with the platform chrome removed, ready to place in an iframe on `origin`. Send the learner in `X-Learner-Id` (an external id or a user id of this organisation). The key must carry the scope of the module the target belongs to (core screens need no module scope), and `origin` must be an exact origin the organisation allows to frame it: the partner's `embed_frame_ancestors` or the `embed.allowed_origins` setting.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["embed"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EmbedSessionsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The origin is not allowed to frame this organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"target":{"type":"string","description":"An embeddable route, e.g. `/feed` or `/courses/<id>/learn`. A query string is allowed."},"origin":{"type":"string","description":"The exact origin of the page that will frame the embed, e.g. `https://app.partner.com`."},"locale":{"type":["string","null"],"description":"Optional language code for the session."}},"required":["target","origin"]}}}}}},"/api/v1/engage/badges":{"get":{"summary":"The learner's badges","description":"Every active badge with `awarded` and `awarded_at` for the calling learner.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageBadgesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/drip/campaigns":{"get":{"summary":"List the learner's drip campaigns","description":"Active drip campaigns visible to the learner (those tied to a published course), each with the number of items the learner has answered and whether the campaign is complete.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageDripCampaignsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/drip/campaigns/{id}/items/{itemId}/complete":{"post":{"summary":"Complete a drip item","description":"Marks one item answered. `confident` records self-assessed recall (\"got it\" true, \"still learning\" false); it defaults to true. Repeating the call for the same item changes the mark and never double-counts.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Campaign id.","schema":{"type":"string","format":"uuid","description":"Campaign id."}},{"name":"itemId","in":"path","required":true,"description":"Item id, from the next-item route.","schema":{"type":"string","format":"uuid","description":"Item id, from the next-item route."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageDripCampaignsIdItemsItemIdCompleteItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confident":{"type":["boolean","null"],"description":"Self-assessed recall. Default true."}},"required":[]}}}}}},"/api/v1/engage/drip/campaigns/{id}/next":{"get":{"summary":"Next drip item","description":"The first item of the campaign the learner has not answered yet, plus the campaign's progress. `item` is null when the campaign is finished.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Campaign id.","schema":{"type":"string","format":"uuid","description":"Campaign id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageDripCampaignsIdNextItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/leaderboard":{"get":{"summary":"Leaderboard","description":"Ranked learners who opted in, within the learner's team or the whole organisation, as far as the organisation's leaderboard setting allows (an organisation set to \"nobody\" returns an empty list). `meta.opted_in` is whether the calling learner appears on it.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"scope","in":"query","required":false,"description":"\"team\" (default) or \"organisation\" (alias \"tenant\").","schema":{"type":"string","enum":["team","organisation","tenant"],"description":"\"team\" (default) or \"organisation\" (alias \"tenant\")."}},{"name":"limit","in":"query","required":false,"description":"Rows to return (1 to 100, default 20; values outside the range are clamped, not refused).","schema":{"type":"integer","description":"Rows to return (1 to 100, default 20; values outside the range are clamped, not refused)."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageLeaderboardItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/microlearning":{"get":{"summary":"Microlearning feed","description":"Active campaigns with all their items (question, answer, order, and whether this learner has answered and how confidently).\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageMicrolearningItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/notification-preferences":{"get":{"summary":"Read the learner's notification preferences","description":"Every notification kind with the channels it can use and the ones currently enabled for this learner. Kinds marked `locked` are transactional and always on.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageNotification-preferencesItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Save the learner's notification preferences","description":"Sets one or more (kind, channel) preferences. Transactional kinds are refused and listed under `rejected`.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageNotification-preferencesPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"type":"array","description":"Array of { kind, channel (\"email\" | \"in_app\" | \"push\"), enabled }."}},"required":["updates"]}}}}}},"/api/v1/engage/notifications":{"get":{"summary":"List the learner's notifications","description":"The learner's in-app notifications, newest first, with `meta.unread_count`. Send the learner in `X-Learner-Id`. `unread=true` returns unread notifications only. `url` is a path inside the platform; a headless integration maps `kind` and `data` to its own screens.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"unread","in":"query","required":false,"description":"Only notifications not yet read.","schema":{"type":"boolean","description":"Only notifications not yet read."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","-created_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageNotificationsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/notifications/{id}/read":{"post":{"summary":"Mark a notification read","description":"Marks one of the learner's own unread notifications read. 404 when it does not exist, is not this learner's, or is already read.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Notification id.","schema":{"type":"string","format":"uuid","description":"Notification id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageNotificationsIdReadItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/notifications/read-all":{"post":{"summary":"Mark every notification read","description":"Marks every unread notification of the learner's as read.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageNotificationsRead-allItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/nuggets/{id}/answer":{"post":{"summary":"Answer a nugget","description":"Records how well the learner recalled the card and schedules its next review with the FSRS-5 algorithm. Send known, or a rating from 1 to 4.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Nugget id from the daily queue.","schema":{"type":"string","description":"Nugget id from the daily queue."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageNuggetsIdAnswerItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"known":{"type":"boolean","description":"True for \"got it\" (FSRS good), false for \"study again\" (FSRS again). Required unless rating is sent."},"rating":{"type":"integer","description":"Optional FSRS rating: 1 again, 2 hard, 3 good, 4 easy. When present it is used instead of known."}},"required":["known","rating"]}}}}}},"/api/v1/engage/nuggets/daily":{"get":{"summary":"Today's nuggets","description":"Flashcards due today for the learner (new cards first, then overdue), from lessons and from cards the learner made. The `id` is opaque; pass it to the answer route.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many cards to return (1 to 50; values outside the range are clamped, not refused). Defaults to the organisation setting.","schema":{"type":"integer","description":"How many cards to return (1 to 50; values outside the range are clamped, not refused). Defaults to the organisation setting."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageNuggetsDailyItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/points":{"get":{"summary":"The learner's points","description":"Ledger entries for the calling learner (lesson_completed, course_completed, badge_earned), newest first. `meta.total_points` is the sum over the whole ledger.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","-created_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngagePointsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/recommendations":{"get":{"summary":"Course recommendations","description":"Ranked course recommendations for the learner, each with the human-readable reasons behind it.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"How many to return (1 to 20, default 8; values outside the range are clamped, not refused).","schema":{"type":"integer","description":"How many to return (1 to 20, default 8; values outside the range are clamped, not refused)."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EngageRecommendationsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/engage/streak":{"get":{"summary":"The learner's streak","description":"Current and longest streak in days, and the last day of activity.\n\nRequires scope `module.engage` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["engage"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EngageStreakItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/events/registrations":{"get":{"summary":"List session registrations","description":"Registrations (confirmed, waitlisted or cancelled) for the caller's tenant.\n\nRequires scope `module.events` and permission `course.edit.any` (see \"Auth\" in the module document's description).","tags":["events"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"session_id","in":"query","required":false,"description":"Filter to one session.","schema":{"type":"string","format":"uuid","description":"Filter to one session."}},{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"status","in":"query","required":false,"description":"Filter to this exact status (\"confirmed\", \"waitlisted\", \"cancelled\").","schema":{"type":"string","description":"Filter to this exact status (\"confirmed\", \"waitlisted\", \"cancelled\")."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: registered_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["registered_at","-registered_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EventsRegistrationsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Register for a session","description":"Registers the learner named in `X-Learner-Id` for a session. Confirms if capacity remains, otherwise waitlists — the same rule and the same database function the web app's self-booking path uses, so results never diverge.\n\nRequires scope `module.events` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["events"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EventsRegistrationsPostItem"}},"required":["data"]}}}},"400":{"description":"Missing session_id, or the RPC refused the registration for a reason other than \"not found\".","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No session with that id in this tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The api_register_for_session() database function has not been applied to this server yet.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string","format":"uuid","description":"Session to register for."}},"required":["session_id"]}}}}}},"/api/v1/events/registrations/{id}/cancel":{"post":{"summary":"Cancel a session registration","description":"Cancels the learner's own registration. A confirmed seat that frees up is given to the first person on the waitlist.\n\nRequires scope `module.events` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["events"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Registration id.","schema":{"type":"string","format":"uuid","description":"Registration id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EventsRegistrationsIdCancelItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/events/sessions":{"get":{"summary":"List sessions","description":"Classroom and virtual sessions for the caller's tenant.\n\nRequires scope `module.events` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["events"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Filter to sessions with this exact status.","schema":{"type":"string","description":"Filter to sessions with this exact status."}},{"name":"course_id","in":"query","required":false,"description":"Filter to sessions for one course.","schema":{"type":"string","format":"uuid","description":"Filter to sessions for one course."}},{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against title.","schema":{"type":"string","description":"Case-insensitive substring match against title."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: starts_at, created_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["starts_at","created_at","-starts_at","-created_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EventsSessionsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/events/sessions/{id}/attendance":{"post":{"summary":"Mark session attendance","description":"Marks a learner attended (true) or absent (false) for a session. Idempotent: marking again overwrites.\n\nRequires scope `module.events` and permission `course.edit.any` (see \"Auth\" in the module document's description).","tags":["events"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Session id.","schema":{"type":"string","format":"uuid","description":"Session id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EventsSessionsIdAttendanceItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"learner":{"type":"string","description":"The learner's external id or user id."},"attended":{"type":"boolean","description":"True if they attended."}},"required":["learner","attended"]}}}}}},"/api/v1/events/sessions/{id}/attendance/claim":{"post":{"summary":"Claim completion for an attended session","description":"Completes the learner's linked lesson once a trainer has marked them attended. `result` is the database function's own answer.\n\nRequires scope `module.events` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["events"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Session id.","schema":{"type":"string","format":"uuid","description":"Session id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EventsSessionsIdAttendanceClaimItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/graphql":{"post":{"summary":"Read-only GraphQL endpoint","description":"One POST endpoint over the same read models as the REST routes, with the same scope and permission checks applied to each root field. Queries only — there are no mutations or subscriptions. Introspection is disabled; the schema is published as SDL at /api/v1/graphql/schema.graphql. Queries are bounded by depth and by estimated cost; see /api/v1/limits for the current bounds.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["meta"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GraphqlItem"}},"required":["data"]}}}},"400":{"description":"A construct outside the supported subset: a mutation, a fragment, a variable, a directive, or a second operation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","description":"The GraphQL query document. One query operation, no variables, no fragments."},"operationName":{"type":["string","null"],"description":"Accepted and ignored — a document may only contain one operation."}},"required":["query"]}}}}}},"/api/v1/hooks":{"get":{"summary":"List this key's REST Hook subscriptions","description":"Webhook endpoints this API key created via POST /api/v1/hooks. Does not include endpoints created on /admin/webhooks or by a different key.\n\nRequires scope `(none — scoped to the calling key's own hooks, not a module)` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["hooks"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","-created_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HooksItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Subscribe to an event (REST Hooks)","description":"Creates a webhook endpoint owned by this key for one event. This is the \"subscribe\" half of the REST Hooks pattern Zapier (and, over plain HTTP, Make/n8n) use: call this when a Zap/scenario/workflow is turned on, and DELETE /api/v1/hooks/{id} when it is turned off.\n\nRequires scope `the event's module (resolved from the request body's `event` field — see description)` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["hooks"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HooksPostItem"}},"required":["data"]}}}},"400":{"description":"Missing target_url/event, an unrecognised event, an invalid source, or a target_url the destination guard refused.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"target_url":{"type":"string","format":"uri","description":"https:// URL to deliver to. Refused if it resolves to a private/loopback/link-local address (same destination guard /admin/webhooks uses)."},"event":{"type":"string","description":"One event type from the catalogue (see docs/webhooks.md / the admin page's picker). Determines the module this key must be scoped for and entitled to."},"source":{"type":"string","enum":["api","zapier","make","n8n"],"description":"What is creating this hook. Defaults to \"api\"."}},"required":["target_url","event","source"]}}}}}},"/api/v1/hooks/{id}":{"delete":{"summary":"Unsubscribe","description":"Deletes a hook this key created. Another key's hook (or a nonexistent id) answers 404.\n\nRequires scope `(none — ownership check: api_key_id = the calling key)` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["hooks"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Hook id, from POST /api/v1/hooks or GET /api/v1/hooks.","schema":{"type":"string","format":"uuid","description":"Hook id, from POST /api/v1/hooks or GET /api/v1/hooks."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HooksIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No hook with that id owned by this key in this tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/hooks/samples":{"get":{"summary":"Sample payloads for an event","description":"The tenant's last 3 real delivery payloads for this event, or one synthetic example when there is no delivery history yet.\n\nRequires scope `the event's module (resolved from the `event` query parameter — see description)` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["hooks"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"event","in":"query","required":true,"description":"One event type from the catalogue.","schema":{"type":"string","description":"One event type from the catalogue."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HooksSamplesItem"}},"required":["data"]}}}},"400":{"description":"Missing or unrecognised `event`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/insights/at-risk":{"get":{"summary":"List at-risk learners","description":"Enrolments flagged by the explainable at-risk rule (no activity in 14 days AND past the halfway point AND due within 7 days). Only enrolments with a due date are evaluated — see the \"note\" field in the response envelope.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["insights"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Filter to one course.","schema":{"type":"string","format":"uuid","description":"Filter to one course."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: due_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["due_at","-due_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InsightsAt-riskItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/insights/completions":{"get":{"summary":"List completion records","description":"Immutable, snapshotted course-completion records for the tenant, for reporting/insights-scoped keys.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["insights"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Filter to completions originally taken on this course.","schema":{"type":"string","format":"uuid","description":"Filter to completions originally taken on this course."}},{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: completed_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["completed_at","-completed_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InsightsCompletionsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/insights/kirkpatrick":{"get":{"summary":"Kirkpatrick evaluation","description":"The four Kirkpatrick levels for one course (`course_id`) or organisation-wide. Level 4 rows are customer-supplied business figures and are tagged as such.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["insights"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Limit levels 1, 2 and 4 to this course. Level 3 is organisation-wide regardless.","schema":{"type":"string","format":"uuid","description":"Limit levels 1, 2 and 4 to this course. Level 3 is organisation-wide regardless."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InsightsKirkpatrickItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/insights/reporting/{dataset}":{"get":{"summary":"Reporting dataset (BI pull)","description":"One reporting dataset for the organisation, for BI tools (Power BI, Tableau, a warehouse loader). Datasets: daily_metrics, course_engagement, programme_outcomes, enrolments, learner_enrolments (learner level; off unless the organisation turns it on). Values whose group is smaller than the organisation minimum are null with `suppressed: true`. `format=json` (default) returns the v1 envelope; `format=csv` returns text/csv with the next cursor in `X-Next-Cursor`. `page_size` defaults to 1000 and may be up to 10000 on this endpoint. The range defaults to the 31 days ending yesterday and is limited to 400 days.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["insights"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"dataset","in":"path","required":true,"description":"Which dataset.","schema":{"type":"string","enum":["daily_metrics","course_engagement","programme_outcomes","enrolments","learner_enrolments"],"description":"Which dataset."}},{"name":"from","in":"query","required":false,"description":"First UTC day (inclusive). Default: 30 days before `to`.","schema":{"type":"string","format":"date","description":"First UTC day (inclusive). Default: 30 days before `to`."}},{"name":"to","in":"query","required":false,"description":"Last UTC day (inclusive). Default: yesterday.","schema":{"type":"string","format":"date","description":"Last UTC day (inclusive). Default: yesterday."}},{"name":"format","in":"query","required":false,"description":"json (default) or csv. parquet answers 501 format_unavailable.","schema":{"type":"string","enum":["json","csv","parquet"],"description":"json (default) or csv. parquet answers 501 format_unavailable."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InsightsReportingDatasetItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"learner_enrolments while learner-level reporting is off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"501":{"description":"format=parquet: not available yet; use csv.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/insights/roi":{"get":{"summary":"Return on investment","description":"ROI for one course (`course_id`) or programme (`programme_id`), or organisation-wide with neither. Measured facts, the assumptions used and an itemised calculation are returned together with a caveat.\n\nRequires scope `module.insights` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["insights"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Evaluate this course.","schema":{"type":"string","format":"uuid","description":"Evaluate this course."}},{"name":"programme_id","in":"query","required":false,"description":"Evaluate this programme.","schema":{"type":"string","format":"uuid","description":"Evaluate this programme."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InsightsRoiItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/knowledge/answer":{"post":{"summary":"Ask a question and get a cited answer","description":"A short answer to `question` for the learner in X-Learner-Id, written only from sources that learner may read, with numbered citations. `refused` is true (with a `reason`) when no source covers the question. Same quota, guardrails, credits and audit as the signed-in product. Supports `Idempotency-Key`.\n\nRequires scope `module.knowledge` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeAnswerItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, or this feature, is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The locked guardrails could not be loaded; the call was refused.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","description":"The question (1 to 500 characters)."}},"required":["question"]}}}}}},"/api/v1/knowledge/articles":{"get":{"summary":"List articles","description":"Published articles, most recently updated first. Filter by `category_id`, `tag`, `featured`, or `author_id` (`me` for the learner's own). `status=draft` or `archived` lists the learner's own (or, with `knowledge.publish`, everyone's) articles in that state. The list omits `body`; read one article for it.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"category_id","in":"query","required":false,"description":"Only this category.","schema":{"type":"string","format":"uuid","description":"Only this category."}},{"name":"tag","in":"query","required":false,"description":"Only articles carrying this tag.","schema":{"type":"string","description":"Only articles carrying this tag."}},{"name":"featured","in":"query","required":false,"description":"true: only featured articles.","schema":{"type":"boolean","description":"true: only featured articles."}},{"name":"author_id","in":"query","required":false,"description":"`me`, or an author's user id.","schema":{"type":"string","description":"`me`, or an author's user id."}},{"name":"status","in":"query","required":false,"description":"Default `published`.","schema":{"type":"string","enum":["draft","published","archived"],"description":"Default `published`."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeArticlesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Write an article","description":"Creates an article as the learner, who needs `knowledge.author` (403 otherwise). `status` defaults to `draft`; `published` additionally needs `knowledge.publish` (403 otherwise). The slug is generated from the title. Returns the article.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","description":"Title."},"body":{"type":"string","description":"Article text (Markdown)."},"summary":{"type":["string","null"],"description":"Short summary."},"category_id":{"type":["string","null"],"format":"uuid","description":"Category id."},"tags":{"type":["array","null"],"description":"Tag names (up to 20, lower-cased)."},"status":{"type":["string","null"],"enum":["draft","published","archived"],"description":"Default `draft`."},"is_featured":{"type":["boolean","null"],"description":"Feature it on the knowledge home."}},"required":["title","body"]}}}}}},"/api/v1/knowledge/articles/{id}":{"delete":{"summary":"Delete an article","description":"Deletes an article: its author while it is not published, or any holder of `knowledge.publish`. 404 otherwise.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get one article","description":"One article by id or slug, with its body, reaction counts, the learner's own reaction and the comment count. Records a view.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdGetItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Edit an article","description":"Edits an article: its author (with `knowledge.author`) or any holder of `knowledge.publish`. Moving it to `published` needs `knowledge.publish` (403 otherwise). Returns the article.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":["string","null"],"description":"New title."},"body":{"type":["string","null"],"description":"New text."},"summary":{"type":["string","null"],"description":"New summary; null clears it."},"category_id":{"type":["string","null"],"format":"uuid","description":"New category; null clears it."},"tags":{"type":["array","null"],"description":"Replacement tag list."},"status":{"type":["string","null"],"enum":["draft","published","archived"],"description":"New state."},"is_featured":{"type":["boolean","null"],"description":"Feature or unfeature."}},"required":[]}}}}}},"/api/v1/knowledge/articles/{id}/comments":{"get":{"summary":"List comments on an article","description":"Comments and replies on an article, oldest first, flat.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeArticlesIdCommentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Comment on an article","description":"Adds a comment, or a reply when `parent_id` is given. Returns the comment.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdCommentsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"Comment text."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The comment replied to."}},"required":["body"]}}}}}},"/api/v1/knowledge/articles/{id}/comments/{commentId}":{"delete":{"summary":"Delete a comment","description":"Deletes the learner's own comment, or any comment for a holder of `knowledge.publish`.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"commentId","in":"path","required":true,"description":"Comment id.","schema":{"type":"string","format":"uuid","description":"Comment id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdCommentsCommentIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Edit a comment","description":"Edits the learner's own comment. Returns the comment.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"commentId","in":"path","required":true,"description":"Comment id.","schema":{"type":"string","format":"uuid","description":"Comment id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdCommentsCommentIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"New comment text."}},"required":["body"]}}}}}},"/api/v1/knowledge/articles/{id}/reactions":{"delete":{"summary":"Remove a reaction","description":"Removes the learner's reaction on the article. Idempotent.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdReactionsItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Mark an article helpful or not helpful","description":"Sets the learner's reaction, replacing the previous one. Idempotent.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The article's id (uuid) or its slug.","schema":{"type":"string","description":"The article's id (uuid) or its slug."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeArticlesIdReactionsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["helpful","not_helpful"],"description":"The reaction."}},"required":["type"]}}}}}},"/api/v1/knowledge/categories":{"get":{"summary":"List knowledge categories","description":"The knowledge base categories in display order. Categories are managed by an administrator in the admin portal. One page; `meta.cursor.next` is always null.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeCategoriesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/knowledge/search":{"get":{"summary":"Search articles","description":"Published articles whose title or summary contains `q` (case-insensitive), most viewed first. 400 when `q` is empty after cleaning.\n\nRequires scope `module.knowledge` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["knowledge"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"q","in":"query","required":true,"description":"The search term.","schema":{"type":"string","description":"The search term."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSearchItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/anything":{"post":{"summary":"Explain a topic from the organisation's sources","description":"A short, cited explainer for the learner in X-Learner-Id, written only from the sources that learner may read. `status` is `ok`, `not_found` (nothing in the sources; `general_allowed` says whether `mode: \"general\"` may be tried) , or `disabled`/`daily_limit` (returned as errors). The general mode is never used unless asked for. Same quota, guardrails, credits and audit as the signed-in product. Supports `Idempotency-Key`.\n\nRequires scope `module.ai` and permission `ai.use` (see \"Auth\" in the module document's description).","tags":["ai"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnAnythingItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The credit balance cannot cover the call.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"AI, Learn anything, or the general mode is switched off for the organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The AI allowance, or the learner's daily AI practice limit, is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The locked guardrails could not be loaded; the call was refused.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"topic":{"type":"string","description":"What the learner wants to understand (2 to 300 characters)."},"mode":{"type":["string","null"],"description":"`grounded` (default) or `general`. `general` answers from general AI knowledge, labelled, and only when the organisation allows it."}},"required":["topic"]}}}}}},"/api/v1/learn/courses":{"get":{"summary":"List courses","description":"Courses visible to the caller's tenant, newest-created first by default.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Filter to courses with this exact status.","schema":{"type":"string","description":"Filter to courses with this exact status."}},{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against title.","schema":{"type":"string","description":"Case-insensitive substring match against title."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, updated_at, title, duration_minutes. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","updated_at","title","duration_minutes","-created_at","-updated_at","-title","-duration_minutes"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LearnCoursesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/courses/{id}":{"get":{"summary":"Get one course","description":"A single course by id, scoped to the caller's tenant.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Course id.","schema":{"type":"string","format":"uuid","description":"Course id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnCoursesIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/courses/{id}/lessons":{"get":{"summary":"List a course's modules and lessons","description":"A course's modules and lessons in order (id, title, duration, order), with no learner progress and no lesson content. Use `GET /api/v1/learn/lessons/{id}` with a learner for the content.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Course id.","schema":{"type":"string","format":"uuid","description":"Course id."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnCoursesIdLessonsItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/courses/{id}/outline":{"get":{"summary":"A course outline with the learner's progress","description":"Modules and lessons of a course with the learner named in `X-Learner-Id`: per-lesson `completed`, `position_seconds` (resume point), and `locked` / `lock_reason` (`access_expired` or `views_spent` for a rental or pay-per-view grant that has run out). Also the learner's enrolment, if any, and a progress summary. A course the learner cannot see answers 404.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Course id.","schema":{"type":"string","format":"uuid","description":"Course id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnCoursesIdOutlineItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/enrolments":{"get":{"summary":"List enrolments","description":"Enrolments across the caller's tenant. Permission: learner.progress.tenant (the existing permission key that already gates cross-learner progress reads elsewhere; this route does not invent a narrower one).\n\nRequires scope `module.learn` and permission `learner.progress.tenant` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":false,"description":"Filter to one course.","schema":{"type":"string","format":"uuid","description":"Filter to one course."}},{"name":"completed","in":"query","required":false,"description":"true = completed_at is set, false = not yet completed.","schema":{"type":"boolean","description":"true = completed_at is set, false = not yet completed."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: enrolled_at, completed_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["enrolled_at","completed_at","-enrolled_at","-completed_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LearnEnrolmentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Enrol a person in a course","description":"Enrols (assigns) an existing active member of the organisation in one of the organisation's own courses, or a course shared with it. Idempotent: assigning again updates the due date. Returns 201 with the enrolment. Audited as course.enrol.\n\nRequires scope `module.learn` and permission `course.assign` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnEnrolmentsPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The person is outside the delegated scope of the principal this key acts as.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such course_id available to this organisation, the person is not an active member, or `email` did not resolve to an existing member (use POST /api/v1/org/users to invite someone new instead).","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"The course is not published.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"Only on a server where the api_assign_course() database function has not been applied.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Learner to enrol. Omit if using `email` instead."},"email":{"type":"string","format":"email","description":"Resolves to an existing member's user_id (via the same lookup invitations use). Ignored if `user_id` is given."},"course_id":{"type":"string","format":"uuid","description":"Course to enrol in."},"due_at":{"type":["string","null"],"format":"date-time","description":"Optional due date."}},"required":["user_id","email","course_id"]}}}}}},"/api/v1/learn/enrolments/self":{"post":{"summary":"Enrol the learner in a course (self-service)","description":"Enrols the learner in `X-Learner-Id` in a course they can see. Returns `mode: enrolled` (201 on a new enrolment, 200 when they already were) with the enrolment, or, when the course requires approval or has self-enrolment closed, `mode: pending` (202) with the `request_id` of the enrolment request that was filed and the approvers notified. Idempotent. A course the learner cannot see answers 404.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnEnrolmentsSelfItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"Course to enrol in."},"note":{"type":["string","null"],"description":"Optional note sent with an approval request."}},"required":["course_id"]}}}}}},"/api/v1/learn/lessons/{id}":{"get":{"summary":"Get a lesson as the learner sees it","description":"The lesson's content blocks for the learner named in `X-Learner-Id`, with scored-quiz answer keys excluded. `media_urls` maps every `storage:` reference inside `content` to a short-lived signed URL (`media_urls_expire_in` seconds). If the learner's locale has a published translation, `content` is that translation and `content_locale` says so; pass `?locale=` to choose one. A lesson whose course the learner cannot open (an expired rental or spent pay-per-view grant) answers 403 `lesson_locked`.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Lesson id.","schema":{"type":"string","format":"uuid","description":"Lesson id."}},{"name":"locale","in":"query","required":false,"description":"BCP 47 tag of the translation to prefer. Defaults to the learner's own locale.","schema":{"type":"string","description":"BCP 47 tag of the translation to prefer. Defaults to the learner's own locale."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnLessonsIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The learner cannot open this lesson right now; `reason` is `access_expired` or `views_spent`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/lessons/{id}/progress":{"put":{"summary":"Record lesson progress","description":"Records progress for the learner in `X-Learner-Id`. `action: complete` marks the lesson complete if the course's rules allow it (otherwise `completed` is false and `reason` says why, e.g. `below_pass_mark`, `attempts_exhausted`, `blocks_incomplete`); `heartbeat` adds `seconds` of time-on-task (and an optional `position_seconds` resume point); `block` records `value` for the block `block_id` of `kind` video, continue or flashcards. Always returns `completed` and `course_completed`.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Lesson id.","schema":{"type":"string","format":"uuid","description":"Lesson id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnLessonsIdProgressItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The learner cannot open this lesson right now (expired rental or spent views), so nothing is recorded; `reason` is `access_expired` or `views_spent`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"The course is a draft; draft courses record no progress.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["complete","heartbeat","block"],"description":"Which progress write to make."},"seconds":{"type":"integer","description":"heartbeat: seconds of activity since the last beat (0..90)."},"position_seconds":{"type":["integer","null"],"description":"heartbeat: the video/audio resume position."},"block_id":{"type":"string","description":"block: the block id."},"kind":{"type":"string","enum":["video","continue","flashcards"],"description":"block: video, continue or flashcards."},"value":{"type":"number","description":"block: progress value, 0..1 for video, 1 for continue and flashcards. Default 1."}},"required":["action","seconds","block_id","kind","value"]}}}}}},"/api/v1/learn/lessons/{id}/quiz-answers":{"post":{"summary":"Submit a quiz answer","description":"Submits the learner's answer to one question of a scored quiz block. Single answer: `answer` is the zero-based index of the chosen option. Every other question type sends `response` instead and is graded exactly as the player grades it: multiple answer `{indexes:[...]}`, fill in the blank `{text}`, matching `{choices:[index into the question's matchChoices, per pair]}`, short free text `{text, self_mark}` (without self_mark a scored free-text question answers reason `self_mark_required` with `model_answer` and records nothing). Typed results add `score` (0..1, partial credit) and `answer_key` under the same reveal rule. `question_index` (default 0) selects the question in a multi-question block. Returns `accepted`, `reason` (null, or `already_answered`, `already_completed`, `attempts_exhausted`, `not_found`, `invalid_selection`), `is_correct`, `attempt_no`, `attempts_used`, `max_attempts`, `attempts_remaining` and `correct_answer` (set only when the answer was right, on the last attempt, or once the pass is final; a wrong answer with attempts left says only that it was wrong).\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Lesson id.","schema":{"type":"string","format":"uuid","description":"Lesson id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnLessonsIdQuiz-answersItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The learner cannot open this lesson right now (expired rental or spent views), so no answer is recorded; `reason` is `access_expired` or `views_spent`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"block_id":{"type":"string","description":"The quiz block id."},"answer":{"type":"integer","description":"Single answer: zero-based index of the chosen option."},"response":{"type":"object","description":"Typed questions: {indexes} | {text, self_mark?} | {choices}. Use instead of answer."},"question_index":{"type":"integer","description":"Question within the block. Default 0."}},"required":["block_id","answer","response","question_index"]}}}}}},"/api/v1/learn/scorm/{packageId}/attempt":{"get":{"summary":"Read a learner's SCORM attempt","description":"The stored SCORM data-model state of the learner in `X-Learner-Id` for one package: lesson_status, score, suspend_data, lesson_location, session_time, exit and entry. `attempt` is null when none was started. For a SCORM 2004 package the SCO list (`scos`, with each SCO's status) and the SCO to resume on (`current`) are included. Needs the standards module (`module.standards`).\n\nRequires scope `module.standards` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"packageId","in":"path","required":true,"description":"SCORM package id.","schema":{"type":"string","format":"uuid","description":"SCORM package id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnScormPackageIdAttemptItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Start or resume a SCORM attempt","description":"Resume the learner's current attempt for the package, or start a new numbered attempt when the package's attempt policy says so, and return its `attemptId`, which POST .../track needs. Needs the standards module (`module.standards`).\n\nRequires scope `module.standards` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"packageId","in":"path","required":true,"description":"SCORM package id.","schema":{"type":"string","format":"uuid","description":"SCORM package id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnScormPackageIdAttemptPostItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The learner cannot open the package's course right now (expired rental or spent views); no attempt is started.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/learn/scorm/{packageId}/track":{"post":{"summary":"Record a SCORM call","description":"For a SCORM 1.2 package send `action` `initialize`, `setValue` (with `key` and `value`, e.g. `cmi.core.lesson_status`), `commit` or `finish`. For a SCORM 2004 package send `action: sync2004` with `event` (`initialize`, `commit`, `terminate`, `status` or `visit`), `sco_id` and a `snapshot` of the SCO's run-time data (a 1.2 action against a 2004 package, or the reverse, is refused). `attempt_id` comes from POST .../attempt. The response carries the completion outcome when the status became completed or passed. Needs the standards module (`module.standards`).\n\nRequires scope `module.standards` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["learn"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"packageId","in":"path","required":true,"description":"SCORM package id.","schema":{"type":"string","format":"uuid","description":"SCORM package id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LearnScormPackageIdTrackItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The learner cannot open the package's course right now (expired rental or spent views); nothing is recorded.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attempt_id":{"type":"string","format":"uuid","description":"The learner's attempt for this package."},"action":{"type":"string","description":"1.2: initialize, setValue, commit, finish. 2004: sync2004."},"key":{"type":"string","description":"1.2 setValue: the cmi.* element."},"value":{"type":"string","description":"1.2 setValue: the value."},"event":{"type":"string","description":"2004 sync2004: initialize, commit, terminate, status or visit."},"sco_id":{"type":"string","description":"2004 sync2004: the SCO identifier from the attempt read."},"snapshot":{"type":"object","description":"2004 sync2004: the SCO run-time data snapshot."}},"required":["attempt_id","action","key","value","event","sco_id","snapshot"]}}}}}},"/api/v1/limits":{"get":{"summary":"Published rate limits and query bounds","description":"The rate limit every API key is held to, the window it is counted over, and the two knobs that move it. Send a valid Authorization: Bearer key to additionally receive your own organisation's effective ceiling. Presenting a key here consumes no quota.\n\nNo API key required — this endpoint exists to be readable before you have one.","tags":["meta"],"security":[],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LimitsItem"}},"required":["data"]}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/profile":{"get":{"summary":"Read the learner's profile","description":"The learner's self-service profile fields.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["me"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MeProfileItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update the learner's profile","description":"Changes any of display_name, avatar_url, title, bio, expertise, locale, timezone, leaderboard_opt_in. Unknown fields are refused.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["me"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MeProfileItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"display_name":{"type":["string","null"],"description":"Up to 200 characters."},"avatar_url":{"type":["string","null"],"description":"https URL, or null to clear."},"title":{"type":["string","null"],"description":"Up to 200 characters."},"bio":{"type":["string","null"],"description":"Up to 2000 characters, or null."},"expertise":{"type":["array","null"],"description":"Up to 50 strings."},"locale":{"type":["string","null"],"description":"Language code, or null."},"timezone":{"type":["string","null"],"description":"IANA timezone, or null."},"leaderboard_opt_in":{"type":["boolean","null"],"description":"Appear on leaderboards."}},"required":[]}}}}}},"/api/v1/me/record":{"get":{"summary":"The learner's learning record","description":"Courses, paths, programmes, sessions, assessments, certificates, credits, badges and external training the learner has on record. `year`, `type` and `status` filter server-side. `meta.warnings` names any source that could not be read.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["me"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"year","in":"query","required":false,"description":"Only rows dated in this year.","schema":{"type":"integer","description":"Only rows dated in this year."}},{"name":"type","in":"query","required":false,"description":"\"course\", \"path\", \"programme\", \"session\", \"assessment\", \"certificate\", \"credit\", \"badge\" or \"external_claim\".","schema":{"type":"string","description":"\"course\", \"path\", \"programme\", \"session\", \"assessment\", \"certificate\", \"credit\", \"badge\" or \"external_claim\"."}},{"name":"status","in":"query","required":false,"description":"Exact status text, e.g. \"Completed\".","schema":{"type":"string","description":"Exact status text, e.g. \"Completed\"."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MeRecordItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/timeline":{"get":{"summary":"The learner's timeline","description":"Learning events for the calling learner, newest first.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["me"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MeTimelineItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/me/transcript":{"get":{"summary":"The learner's transcript","description":"Completed items (courses, certificates, sessions attended, credits, badges...) with their verification codes, and the credit awards with totals by accreditation body and year.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["me"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MeTranscriptItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/org/departments":{"get":{"summary":"List departments","description":"Departments (and other org-unit types) for the caller's tenant, including the parent_id tree structure.\n\nRequires scope `module.org` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["org"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against name.","schema":{"type":"string","description":"Case-insensitive substring match against name."}},{"name":"type","in":"query","required":false,"description":"Filter to this exact org-unit type (e.g. \"department\").","schema":{"type":"string","description":"Filter to this exact org-unit type (e.g. \"department\")."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, name. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","name","-created_at","-name"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrgDepartmentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/org/groups":{"get":{"summary":"List groups","description":"Groups (static and dynamic) for the caller's tenant.\n\nRequires scope `module.org` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["org"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against name.","schema":{"type":"string","description":"Case-insensitive substring match against name."}},{"name":"is_dynamic","in":"query","required":false,"description":"Filter to dynamic (true) or static (false) groups.","schema":{"type":"boolean","description":"Filter to dynamic (true) or static (false) groups."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, name. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","name","-created_at","-name"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrgGroupsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/org/groups/{id}/members":{"post":{"summary":"Add a person to a group","description":"Adds one person to a static group (group_members insert). Refused for a dynamic group — its membership is computed from `rule`, never stored.\n\nRequires scope `module.org` and permission `settings.manage` (see \"Auth\" in the module document's description).","tags":["org"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Group id, from GET /api/v1/org/groups.","schema":{"type":"string","format":"uuid","description":"Group id, from GET /api/v1/org/groups."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrgGroupsIdMembersItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The person is outside the delegated scope of the principal this key acts as.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"No such group in this tenant, or user_id is not an active member of it.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Person to add."}},"required":["user_id"]}}}}}},"/api/v1/org/users":{"get":{"summary":"List organisation members","description":"Member directory for the caller's tenant: profile fields and department/manager links. No auth identity or email is included.\n\nRequires scope `module.org` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["org"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"department_id","in":"query","required":false,"description":"Filter to members of this department.","schema":{"type":"string","format":"uuid","description":"Filter to members of this department."}},{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against full name.","schema":{"type":"string","description":"Case-insensitive substring match against full name."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, full_name. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","full_name","-created_at","-full_name"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrgUsersItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Invite a person","description":"Invites someone into the organisation by email — the same lib/invitations/service.ts#createInvitation the admin People page's \"Invite\" button calls, so behaviour (role validation, the tenant_admin escalation guard, the duplicate-pending-invite refusal, the invitation email) is identical.\n\nRequires scope `module.org` and permission `user.invite` (see \"Auth\" in the module document's description).","tags":["org"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/OrgUsersPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"The organisation is at its plan's member limit (body carries `limit`, `value`, `used`, `upgrade_to`; pending invitations count).","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Granting the administrator role needs tenant.admin, or the role carries permissions the key does not hold.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"This email is already a member (`reason: already_member`) or already has a pending invitation (`reason: already_invited`, body carries the existing invitation's `id`).","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"The organisation's daily invitation allowance is used up.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Address to invite."},"roleKey":{"type":"string","description":"A tenant_roles.key from this organisation. Granting \"tenant_admin\" requires the key's acting principal to also hold tenant.admin."},"departmentId":{"type":["string","null"],"format":"uuid","description":"Optional department to place the person in once they accept."}},"required":["email","roleKey"]}}}}}},"/api/v1/partner/ai-keys":{"delete":{"summary":"Remove an AI provider key","description":"Deletes the partner's stored key. Child organisations that ran on it fall back at once to the platform key and are charged their full AI credits.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"provider","in":"query","required":true,"description":"The provider whose key to remove.","schema":{"type":"string","description":"The provider whose key to remove."}},{"name":"module_key","in":"query","required":false,"description":"The module the key is limited to; omit for the provider default.","schema":{"type":"string","description":"The module the key is limited to; omit for the provider default."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerAi-keysItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"List the partner's AI provider keys","description":"The AI provider keys the partner holds and how they are shared with child organisations. The key itself is never returned.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerAi-keysGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Store or update an AI provider key","description":"Creates or updates the partner's key for one provider and (optionally) one module. `api_key` is write-only and required when creating; omit it on an update to keep the stored one. Child organisations run on this key when they have no key of their own and `inherit_to_children` is on; each call then uses the bring-your-own-key share of its normal credits (at least 1), from your partner wallet. `allowed_child_tenants` must be organisations that belong to the partner.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerAi-keysPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"The provider: openrouter, openai, anthropic, azure_openai, google."},"module_key":{"type":["string","null"],"description":"Limit the key to one module; omit or null for the provider default."},"api_key":{"type":"string","description":"The provider API key (write-only)."},"base_url":{"type":"string","description":"The provider endpoint."},"config":{"type":"object","description":"Provider extras, for example the azure_openai `apiVersion`."},"label":{"type":"string","description":"A name for your own reference."},"is_active":{"type":"boolean","description":"Whether the key is used. Default true."},"inherit_to_children":{"type":"boolean","description":"Share with child organisations. Default true."},"allowed_child_tenants":{"type":["array","null"],"description":"Restrict sharing to these child organisation ids; null = every child."}},"required":["provider","api_key","base_url","config","label","is_active","inherit_to_children"]}}}}}},"/api/v1/partner/content-providers":{"delete":{"summary":"Remove a provider credential","description":"Deletes the partner's stored credential. Child organisations that inherited it lose access at once; items they already synced stay in their catalogues but cannot be added.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"provider_key","in":"query","required":true,"description":"The content provider key to remove.","schema":{"type":"string","description":"The content provider key to remove."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerContent-providersItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"List the partner's content provider credentials","description":"The providers the partner holds a credential for and how they are shared. The credential itself is never returned.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerContent-providersGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Store or update a provider credential","description":"Creates or updates the partner's credential for one provider. `credentials` is write-only and must carry every credential field the provider needs; omit it on an update to keep the stored one. `rights_note` (the contract basis for sharing) is required when creating. `allowed_child_tenants` must be organisations that belong to the partner. Turn `inherit_to_children` on only where the provider contract allows resale or sub-licensing.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerContent-providersPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider_key":{"type":"string","description":"The content provider key."},"credentials":{"type":"object","description":"The provider credential fields (write-only)."},"inherit_to_children":{"type":"boolean","description":"Share with child organisations. Default false."},"allowed_child_tenants":{"type":["array","null"],"description":"Restrict sharing to these child organisation ids; null = every child."},"rights_note":{"type":"string","description":"What the provider contract permits (reseller or sub-licensing basis)."}},"required":["provider_key","credentials","inherit_to_children","rights_note"]}}}}}},"/api/v1/partner/me":{"get":{"summary":"The calling partner","description":"Profile, tier, region, status and contract of the partner this key belongs to, plus the partner role of the person the key acts as. `wallet_balance` is the partner credit wallet balance.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerMeItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/revenue-reports":{"post":{"summary":"Report revenue for a month","description":"Records the partner's revenue for `period` (a month). Omit `tenant_id` for the partner total; send it for one customer organisation. A partner-total figure takes precedence over per-organisation figures. Sending the same period and organisation again replaces the earlier figure. If that month's statement is still a draft it is recomputed.\n\nRequires a partner key scoped `partner.billing`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerRevenue-reportsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"string","description":"The month, `YYYY-MM`."},"tenant_id":{"type":["string","null"],"format":"uuid","description":"A customer organisation of the partner. Omit for the partner total."},"revenue_minor":{"type":"integer","description":"Revenue in minor units (paise, cents), 0 or more."},"currency":{"type":"string","description":"Three-letter code. Must equal the rate card currency to be used."},"notes":{"type":["string","null"],"description":"Optional note for our audit trail."}},"required":["period","revenue_minor","currency"]}}}}}},"/api/v1/partner/statements":{"get":{"summary":"List statements","description":"The partner's monthly statements, newest month first. Statuses: `issued` (awaiting payment), `paid`, `void`, `awaiting_revenue_report` (the month needs a revenue report, see POST /api/v1/partner/revenue-reports). Drafts are not shown. Amounts are minor units (paise, cents) of `currency`.\n\nRequires a partner key scoped `partner.billing`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Only statements in this status.","schema":{"type":"string","enum":["issued","paid","void","awaiting_revenue_report"],"description":"Only statements in this status."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PartnerStatementsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/statements/{id}":{"get":{"summary":"Get a statement","description":"One statement with its lines. Add `?format=csv` for a CSV of the lines (one row per line plus a total row). A draft, or a statement that belongs to another partner, is reported as `404`.\n\nRequires a partner key scoped `partner.billing`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The statement id.","schema":{"type":"string","format":"uuid","description":"The statement id."}},{"name":"format","in":"query","required":false,"description":"Default `json`.","schema":{"type":"string","enum":["json","csv"],"description":"Default `json`."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerStatementsIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/support/tickets":{"get":{"summary":"List support tickets","description":"Support tickets about the partner's customer organisations, newest first, up to 100. Filter with the `tenant_id` and `status` query parameters.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerSupportTicketsItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Open a support ticket","description":"Opens an L2 escalation about one of the partner's customer organisations. `404 tenant_not_child` when the organisation does not belong to this partner.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerSupportTicketsPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tenant_id":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."},"severity":{"type":"string","enum":["low","normal","high","urgent"],"description":"Defaults to normal."},"subject":{"type":"string","description":"Subject line (max 200 characters)."},"body":{"type":"string","description":"Description (max 10000 characters)."},"external_ref":{"type":["string","null"],"description":"Your own reference, echoed back and in webhooks."}},"required":["tenant_id","severity","subject","body"]}}}}}},"/api/v1/partner/tenants":{"get":{"summary":"List customer organisations","description":"The partner's child organisations as aggregates (counts only, never learner rows), newest first.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, name. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","name","-created_at","-name"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PartnerTenantsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Provision a customer organisation","description":"Creates a child organisation under the partner: seeded roles, the chosen tier (no trial, never Free, never paying directly), white-label by default. `products` switches on exactly those products (the rest are off); without it the partner-wide default products apply. An unknown product key is a `422 unknown_product` and nothing is created; a product refused for another reason (`product_requires`, `product_outside_contract`) is reported in `products.error` and the organisation still exists. With `test: true`, or when called with a test key, the organisation is a test (sandbox) customer: never billed, test keys only, at most 3 per partner (`403 sandbox_customer_cap`). Every customer of a sandbox partner is a test customer. Refused when the tier includes a module outside the partner contract (`module_outside_contract`), is above the contract ceiling (`partner_tier_above_contract`), is the free floor (`partner_tier_free`) or a trial (`trial_on_partner_child`), is not a tier (`unknown_tier`), when the child limit is reached (`partner_child_limit`), or when the subdomain is taken (`conflict`). `branding` is validated before anything is created. `domain` claims a custom hostname for the new organisation; if the claim loses a race the organisation still exists and the response says so in `domain`.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`sandbox_customer_cap` — the sandbox already has its 3 test customers. Nothing was charged.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`partner_exiting` — the partner has given notice to leave the programme and cannot add customers.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`unknown_tier` — `tier` is not a tier of the current price list.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Organisation display name."},"subdomain":{"type":"string","description":"Lowercase letters, digits and hyphens, 3 to 63 characters. Must be unused."},"products":{"type":["array","null"],"description":"Product keys to switch on (every other product is off). Omit to apply the partner-wide default products."},"tier":{"type":["string","null"],"description":"The tier key (for example basic, advanced or premium). Omit for the contract default tier."},"plan_key":{"type":["string","null"],"description":"Deprecated: the old name for `tier`."},"branding":{"type":["object","null"],"description":"Optional `{ \"branding.*\": value }` settings applied at creation."},"domain":{"type":["string","null"],"description":"Optional custom hostname to claim for the new organisation."},"region":{"type":["string","null"],"description":"Optional region id. Must be the region this host serves; otherwise 422 `wrong_region` with `region_app_url`."},"test":{"type":["boolean","null"],"description":"Create a test (sandbox) customer. Implied by a test key and by a sandbox partner."}},"required":["name","subdomain"]}}}}}},"/api/v1/partner/tenants/{id}/branding":{"patch":{"summary":"Set a customer organisation's branding","description":"Writes a subset of `branding.*` settings. An unknown key, or a value the setting does not accept, is a `400` and nothing is written.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdBrandingItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"branding.display_name":{"type":["string","null"],"description":"Display name. Shown in the header and emails. Send null to reset to the default."},"branding.primary_color":{"type":["string","null"],"description":"Primary colour. Your main brand colour, used for buttons, links and highlights. It drives the whole app (sign-in, dashboard, course pages, admin portal), not just one screen. Send null to reset to the default."},"branding.accent_color":{"type":["string","null"],"description":"Accent colour. A second colour used alongside the primary colour for badges and highlights. Leave unset to reuse the primary colour everywhere. Send null to reset to the default."},"branding.sidebar_color":{"type":["string","null"],"description":"Navigation colour. Colour of the left navigation rail. Leave empty to use the primary colour. Text on it is chosen automatically for contrast. Send null to reset to the default."},"branding.logo_on_dark_url":{"type":["string","null"],"description":"Logo for dark mode. An alternative logo shown when a person uses dark mode. Leave empty to reuse the main logo. Send null to reset to the default."},"branding.logo_url":{"type":["string","null"],"description":"Logo URL. Paste the web address of your logo image. Send null to reset to the default."},"branding.login_headline":{"type":["string","null"],"description":"Sign-in headline. A short line shown above the sign-in form, e.g. \"Welcome to Acme Learning\". Send null to reset to the default."},"branding.landing_headline":{"type":["string","null"],"description":"Landing headline. Headline on your home page when visitors arrive on your own domain. Falls back to your organisation name. Send null to reset to the default."},"branding.landing_subhead":{"type":["string","null"],"description":"Landing subhead. A short supporting line under the headline. Send null to reset to the default."},"branding.landing_cta_label":{"type":["string","null"],"description":"Landing button text. Label for the call-to-action button. Defaults to \"Sign in\". Send null to reset to the default."},"branding.landing_cta_url":{"type":["string","null"],"description":"Landing button link. Where the button goes. Defaults to /login. Send null to reset to the default."},"branding.show_powered_by":{"type":["boolean","null"],"description":"Show the platform name. When off, learner pages, emails, certificates and assistant text use your display name instead of the platform name. Set by the platform for partner organisations. Send null to reset to the default."},"branding.support_email":{"type":["string","null"],"description":"Support email. Where learners are told to write for help. Send null to reset to the default."}},"required":[]}}}}}},"/api/v1/partner/tenants/{id}/catalogue/items/{itemId}/add":{"post":{"summary":"Add a catalogue item to a customer organisation's library","description":"Creates a draft external course in the organisation from a synced catalogue item (201). Idempotent: adding the same item again creates nothing and returns the existing course (200). `403 permission_denied` (`reason: no_provider_licence`) when the organisation has no licence for that provider.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"itemId","in":"path","required":true,"description":"The catalogue item id from the search endpoint.","schema":{"type":"string","format":"uuid","description":"The catalogue item id from the search endpoint."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdCatalogueItemsItemIdAddItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/tenants/{id}/catalogue/search":{"get":{"summary":"Search a customer organisation's content catalogue","description":"Case-insensitive search over the title and description of the organisation's synced provider items. `licence_source` says whether the licence in use is the organisation's own (`tenant`) or inherited from the partner (`partner`). Page with `page_size` (default 25, max 100) and `meta.cursor.next`.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"q","in":"query","required":false,"description":"Search text. Omit to list.","schema":{"type":"string","description":"Search text. Omit to list."}},{"name":"provider_key","in":"query","required":false,"description":"Restrict to one provider.","schema":{"type":"string","description":"Restrict to one provider."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PartnerTenantsIdCatalogueSearchItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/tenants/{id}/chat/install-links":{"post":{"summary":"Create a Slack or Teams install link for a customer","description":"Returns a one-time `install_url` for the customer's Slack or Microsoft 365 administrator. Opening it shows the platform's own approval page; approving connects that workspace to the organisation. The link expires after 72 hours and is spent once a workspace is connected with it. For Teams, the Microsoft 365 organisation must own an email domain the organisation has verified. `403 module_not_entitled` when the organisation's products do not include Slack & Teams; `503 feature_not_provisioned` when the platform is not set up on this server.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdChatInstall-linksItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`module_not_entitled` — the organisation does not include Slack & Teams.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`feature_not_provisioned` — the platform is not set up on this server.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","description":"One of: slack, teams."}},"required":["platform"]}}}}}},"/api/v1/partner/tenants/{id}/chat/installations":{"get":{"summary":"List chat workspaces connected to a customer","description":"The Slack and Microsoft Teams workspaces connected to the organisation, newest first. Disconnected workspaces are left out. `has_error` is true when the last message to the workspace could not be delivered.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdChatInstallationsItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/tenants/{id}/domains":{"post":{"summary":"Claim a custom domain","description":"Claims `hostname` for the organisation. Returns the value the hostname's CNAME record must point at. `409 conflict` when the hostname is already claimed by any organisation.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdDomainsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`conflict` — the hostname is already claimed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hostname":{"type":"string","description":"A hostname like learn.example.com."}},"required":["hostname"]}}}}}},"/api/v1/partner/tenants/{id}/integrations/content-providers":{"delete":{"summary":"Remove a customer organisation's own content-library account","description":"Deletes the organisation's own credential for the provider and sets it to no own account. Items already in its library stay.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"provider_key","in":"query","required":true,"description":"The content provider key.","schema":{"type":"string","description":"The content provider key."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdIntegrationsContent-providersItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"List a customer organisation's content-library accounts","description":"The providers the organisation has set up and whether it holds its own credential for each. The credential itself is never returned.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdIntegrationsContent-providersGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Set a customer organisation's own content-library account","description":"Stores the organisation's own credential for one provider (write-only; every credential field the provider needs) and switches the provider to it. Omit `credentials` to only turn the provider on or off. `403 module_not_entitled` when the organisation's plan does not include Integrations. Catalogue syncs never count anyone as an active user.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdIntegrationsContent-providersPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider_key":{"type":"string","description":"The content provider key."},"credentials":{"type":"object","description":"The organisation's provider credential fields (write-only)."},"enabled":{"type":"boolean","description":"Turn the provider on or off for the organisation. Default true when a credential is stored."}},"required":["provider_key","credentials","enabled"]}}}}}},"/api/v1/partner/tenants/{id}/integrations/hris":{"delete":{"summary":"Remove a customer organisation's HRIS connection","description":"Deletes the connection, its run log and its employee links. People it created stay in the organisation.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"provider","in":"query","required":true,"description":"The HRIS adapter key to disconnect.","schema":{"type":"string","description":"The HRIS adapter key to disconnect."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdIntegrationsHrisItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"List a customer organisation's HRIS connections","description":"The organisation's HRIS connections and the providers it can connect. Provider credentials are never returned.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdIntegrationsHrisGetItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Connect or update a customer organisation's HRIS","description":"Creates or updates the organisation's connection for one HRIS provider, on the customer's or the partner's own provider account. `config` is write-only and required when connecting; omit it on an update to keep the stored one. `rules.default_role_key` is set by the organisation's administrator, not here. `403 module_not_entitled` when the organisation's plan does not include Integrations. Sync runs never count anyone as an active user.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdIntegrationsHrisPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","description":"The HRIS adapter key."},"display_name":{"type":"string","description":"Name shown to the organisation's admins."},"config":{"type":"object","description":"The provider account settings and credentials (write-only)."},"identity_mode":{"type":"string","enum":["pseudonymous","email_allowed"],"description":"Default `pseudonymous`."},"rules":{"type":"object","description":"Sync rules; `default_role_key` is refused."},"schedule":{"type":"string","enum":["hourly","daily","manual"],"description":"Default `daily`."},"status":{"type":"string","enum":["disabled","active"],"description":"Default `disabled`."}},"required":["provider","display_name","config","identity_mode","rules","schedule","status"]}}}}}},"/api/v1/partner/tenants/{id}/keys":{"post":{"summary":"Mint a key for a customer organisation","description":"Creates an API key for the organisation. `scopes` are module scopes (e.g. `module.learn`) that must be inside the partner contract (`403 module_outside_contract`); `permissions` come from the permission catalogue and never include `tenant.admin`, `user.role.assign`, `settings.manage` (`403 permission_denied`). The secret is shown once.\n\nRequires a partner key scoped `partner.keys`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdKeysItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`module_outside_contract` — a scope is outside the partner contract. `permission_denied` — a permission can never be granted to a partner-minted key.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"A label for the key."},"scopes":{"type":"array","description":"Module scopes, at least one."},"permissions":{"type":"array","description":"Permission keys the key may use."},"expires_at":{"type":["string","null"],"format":"date-time","description":"Optional expiry, in the future."}},"required":["name","scopes","permissions"]}}}}}},"/api/v1/partner/tenants/{id}/keys/{keyId}":{"delete":{"summary":"Revoke a customer organisation's key","description":"Revokes the key. `404 not_found` when the key does not exist, belongs to another organisation, or is already revoked.\n\nRequires a partner key scoped `partner.keys`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"keyId","in":"path","required":true,"description":"The key to revoke.","schema":{"type":"string","format":"uuid","description":"The key to revoke."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdKeysKeyIdItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/tenants/{id}/learners":{"post":{"summary":"Create or find a learner","description":"Creates the learner identified by `external_id` in the organisation, or returns the existing one. `email` is refused (`422 email_not_allowed`) when the partner contract is `identity_mode: pseudonymous`; the contract learner cap answers `402 learner_cap`. `role_key` names a tenant role that is not an administrator role; anything else falls back to the organisation's default learner role.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdLearnersItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"`learner_cap` — the contract learner ceiling is reached. `learner_limit_reached` — the plan seat limit is.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`email_in_use` — the address belongs to an account outside this organisation.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`email_not_allowed` — the contract does not allow learner email addresses.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_id":{"type":"string","description":"The partner's own identifier for this person, 1 to 200 characters."},"display_name":{"type":["string","null"],"description":"Optional name. Stored only if sent."},"email":{"type":["string","null"],"format":"email","description":"Optional real address; only when the contract allows it."},"department_id":{"type":["string","null"],"format":"uuid","description":"Optional department in this organisation."},"title":{"type":["string","null"],"description":"Optional job title."},"role_key":{"type":["string","null"],"description":"Optional tenant role key."}},"required":["external_id"]}}}}}},"/api/v1/partner/tenants/{id}/learners/{external_id}":{"delete":{"summary":"Erase a learner (data subject request)","description":"Erases the learner's personal data through the platform's data-subject erasure path and removes the external-id mapping. Answers `202` with the erasure result. A learner under a legal hold is refused with `409 legal_hold` and nothing is deleted. An organisation administrator can never be erased through the API.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"external_id","in":"path","required":true,"description":"The partner's identifier for the learner.","schema":{"type":"string","description":"The partner's identifier for the learner."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdLearnersExternal_idItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"`legal_hold` — the learner is under a legal hold; nothing was deleted.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Update or deactivate a learner","description":"Updates the supplied fields. `active: false` deactivates the learner (reversible with `active: true`); nothing is deleted. A department id must belong to this organisation.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}},{"name":"external_id","in":"path","required":true,"description":"The partner's identifier for the learner.","schema":{"type":"string","description":"The partner's identifier for the learner."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdLearnersExternal_idPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"display_name":{"type":["string","null"],"description":"New display name."},"department_id":{"type":["string","null"],"format":"uuid","description":"New department, or null to clear."},"title":{"type":["string","null"],"description":"New job title."},"active":{"type":["boolean","null"],"description":"false deactivates, true reactivates."}},"required":[]}}}}}},"/api/v1/partner/tenants/{id}/products":{"get":{"summary":"Read a customer organisation's products","description":"Every switchable product with whether it is on for this organisation, plus the partner-wide default products.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdProductsItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Set a customer organisation's products","description":"Replaces the organisation's product set: the products named are on, every other product is off. `null` applies the partner-wide default products. A switch takes effect on the next request: calls into a product that is off answer `403 product_off`, and its embed and LTI launches show a notice. The product's data is kept, read-only, and comes back when it is switched on again. People who used the product earlier in the month are still billed for it. Each product whose state changed sends `product.enabled` or `product.disabled` to the partner's webhooks.\n\nRequires a partner key scoped `partner.tenants`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The customer organisation (must belong to the partner).","schema":{"type":"string","format":"uuid","description":"The customer organisation (must belong to the partner)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerTenantsIdProductsPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`product_outside_contract` — a product is outside the partner contract.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"`products_required` — `products` is null and the partner has no default products.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":["array","null"],"description":"Product keys to switch on. `null` applies the partner-wide default products; `[]` switches every product off."}},"required":[]}}}}}},"/api/v1/partner/usage":{"get":{"summary":"Partner usage","description":"Usage summed over `from`..`to` (inclusive UTC days, default the last 30 days, at most 366), grouped by `tenant`, `feature` or `key`. `ai_credits` is undiscounted. `active_learner_days` is the sum of daily distinct active learners, not a count of distinct people. Grouping by `key` covers API calls only.\n\nRequires a partner key scoped `partner.usage`, acting as a person who currently holds partner role `viewer` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"from","in":"query","required":false,"description":"First day, YYYY-MM-DD (UTC). Default: 29 days before `to`.","schema":{"type":"string","format":"date","description":"First day, YYYY-MM-DD (UTC). Default: 29 days before `to`."}},{"name":"to","in":"query","required":false,"description":"Last day, YYYY-MM-DD (UTC). Default: today.","schema":{"type":"string","format":"date","description":"Last day, YYYY-MM-DD (UTC). Default: today."}},{"name":"group_by","in":"query","required":false,"description":"How to group. Default `tenant`.","schema":{"type":"string","enum":["tenant","feature","key"],"description":"How to group. Default `tenant`."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerUsageItem"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/partner/wallet/topups":{"post":{"summary":"Top up the credit wallet","description":"Retired while partner billing moves to the new pricing: a request that is authorised and well formed answers `410 topups_retired` and creates, collects and credits nothing. Nothing in the wallet changes. The fields below are what the endpoint accepted and returned before it was retired: a top-up of `amount_minor` (minor units of the rate card currency) with a `status` of `pending`, `paid` or `manual`.\n\nRequires a partner key scoped `partner.billing`, acting as a person who currently holds partner role `admin` or higher. Customer-organisation ids that do not belong to the partner answer `404 tenant_not_child`.","tags":["partner"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerWalletTopupsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","description":"The amount to pay, minor units, above 0."}},"required":["amount_minor"]}}}}}},"/api/v1/paths/{id}/progress":{"get":{"summary":"A learner's progress through a learning path","description":"The path, the learner's enrolment (null when not enrolled), the ordered steps with each course's state for this learner (`locked`, `unlocked`, `in_progress`, `done`), `progressPct` and `nextStepId`. Uses the learner in `X-Learner-Id`. A path the learner cannot see answers 404.\n\nRequires scope `module.paths` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["paths"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Learning path id.","schema":{"type":"string","format":"uuid","description":"Learning path id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PathsIdProgressItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/paths/enrolments":{"get":{"summary":"List learning-path enrolments","description":"Who is enrolled in which learning path, for the caller's tenant.\n\nRequires scope `module.paths` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["paths"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"path_id","in":"query","required":false,"description":"Filter to one learning path.","schema":{"type":"string","format":"uuid","description":"Filter to one learning path."}},{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"completed","in":"query","required":false,"description":"\"true\"/\"false\" — filter to completed or in-progress enrolments.","schema":{"type":"string","description":"\"true\"/\"false\" — filter to completed or in-progress enrolments."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: enrolled_at, due_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["enrolled_at","due_at","-enrolled_at","-due_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PathsEnrolmentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/paths/enrolments/self":{"post":{"summary":"Enrol the learner in a learning path (self-service)","description":"Enrols the learner in `X-Learner-Id` in a learning path they can see (public paths, or one they own or were assigned). Idempotent: enrolling twice returns the same enrolment. A path the learner cannot see answers 404.\n\nRequires scope `module.paths` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["paths"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PathsEnrolmentsSelfItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path_id":{"type":"string","format":"uuid","description":"Learning path to enrol in."}},"required":["path_id"]}}}}}},"/api/v1/paths/paths":{"get":{"summary":"List learning paths","description":"Learning paths (ordered course sequences) for the caller's tenant.\n\nRequires scope `module.paths` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["paths"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against title.","schema":{"type":"string","description":"Case-insensitive substring match against title."}},{"name":"is_public","in":"query","required":false,"description":"\"true\"/\"false\" — filter to public or private paths.","schema":{"type":"string","description":"\"true\"/\"false\" — filter to public or private paths."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, updated_at, title. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","updated_at","title","-created_at","-updated_at","-title"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PathsPathsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/podcasts/episodes":{"get":{"summary":"List podcast episodes","description":"Episodes of one feed (`feed_id`) or, without it, of the feeds the learner follows, newest first, with the learner's `position_seconds` and `completed`.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["podcasts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"feed_id","in":"query","required":false,"description":"Only this feed. Omit for the learner's subscriptions.","schema":{"type":"string","format":"uuid","description":"Only this feed. Omit for the learner's subscriptions."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PodcastsEpisodesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/podcasts/episodes/{id}/progress":{"put":{"summary":"Save podcast progress","description":"Saves where the learner stopped in an episode, and optionally whether they finished it. Returns the stored progress.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["podcasts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Episode id.","schema":{"type":"string","format":"uuid","description":"Episode id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PodcastsEpisodesIdProgressItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"position_seconds":{"type":"number","description":"Seconds into the episode (stored as whole seconds)."},"completed":{"type":["boolean","null"],"description":"true marks it finished, false clears that, omitted leaves it unchanged."}},"required":["position_seconds"]}}}}}},"/api/v1/podcasts/feeds":{"get":{"summary":"List podcast feeds","description":"Active podcast feeds of the organisation, each with `subscribed` for the learner. One page; `meta.cursor.next` is always null.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["podcasts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PodcastsFeedsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/podcasts/feeds/{id}/subscribe":{"delete":{"summary":"Unfollow a podcast feed","description":"Removes the learner's subscription. Idempotent.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["podcasts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Feed id.","schema":{"type":"string","format":"uuid","description":"Feed id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PodcastsFeedsIdSubscribeItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Follow a podcast feed","description":"Subscribes the learner to an active feed. Idempotent. 404 when there is no such active feed.\n\nRequires scope `module.learn` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["podcasts"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Feed id.","schema":{"type":"string","format":"uuid","description":"Feed id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PodcastsFeedsIdSubscribeItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/programmes":{"get":{"summary":"List a learner's programmes","description":"The programmes the learner in `X-Learner-Id` is enrolled in, newest start date first. `id` is the programme enrolment id; pass it to `GET /api/v1/programmes/{id}` for the step timeline.\n\nRequires scope `module.paths` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["programmes"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Filter by enrolment status (for example `active` or `completed`).","schema":{"type":"string","description":"Filter by enrolment status (for example `active` or `completed`)."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProgrammesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/programmes/{id}":{"get":{"summary":"A learner's programme and its steps","description":"The programme (name, description, category, start date, status, who assigned it), its steps in order and `progressPct` (required steps done). Each step has `stepType` (course, learning_path, session, nudge, observation_checkpoint), `state` (`done`, `due`, `locked`, `awaiting_session`, `skipped`), `dueAt`, and for a session step the linked session and the learner's registration. Another learner's enrolment, or an unknown id, answers 404.\n\nRequires scope `module.paths` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["programmes"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Programme enrolment id.","schema":{"type":"string","format":"uuid","description":"Programme enrolment id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProgrammesIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/programmes/enrolments":{"post":{"summary":"Enrol a person in a programme","description":"Instantiates the programme template for one person: creates their enrolment and one step instance per template step, dated from `start_date` plus each step's offset. Name the person with `user_id` or `learner` (an external id or user id of this organisation). Idempotent per person, template and start date: repeating the call returns the same enrolment with status 200 (201 when new). Needs `course.assign`.\n\nRequires scope `module.paths` and permission `course.assign` (see \"Auth\" in the module document's description).","tags":["programmes"],"security":[{"ApiKeyAuth":[]}],"parameters":[],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProgrammesEnrolmentsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"The person is outside the delegated scope of the principal this key acts as.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"template_id":{"type":"string","format":"uuid","description":"The programme template (must be active)."},"user_id":{"type":"string","format":"uuid","description":"The person to enrol. Alternative to `learner`."},"learner":{"type":"string","description":"An external id or user id of the person. Alternative to `user_id`."},"start_date":{"type":["string","null"],"format":"date","description":"Programme start (YYYY-MM-DD). Default today (UTC)."}},"required":["template_id","user_id","learner"]}}}}}},"/api/v1/skills/competencies":{"get":{"summary":"List competencies","description":"The competency framework for the caller's tenant (max_level and category per competency).\n\nRequires scope `module.skills` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["skills"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"category","in":"query","required":false,"description":"Filter to this exact category.","schema":{"type":"string","description":"Filter to this exact category."}},{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against name.","schema":{"type":"string","description":"Case-insensitive substring match against name."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, name. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","name","-created_at","-name"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SkillsCompetenciesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/skills/gaps":{"get":{"summary":"The learner's skill gaps","description":"Expected vs actual level per competency for the learner's role (or a target job title), with courses that would close each gap.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["skills"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"target_job_title_id","in":"query","required":false,"description":"Compute against this job title instead of the learner's own.","schema":{"type":"string","format":"uuid","description":"Compute against this job title instead of the learner's own."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SkillsGapsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/skills/scores":{"get":{"summary":"List learner skill scores","description":"Per-learner, per-skill scores for the caller's tenant.\n\nRequires scope `module.skills` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["skills"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"user_id","in":"query","required":false,"description":"Filter to one learner.","schema":{"type":"string","format":"uuid","description":"Filter to one learner."}},{"name":"skill_id","in":"query","required":false,"description":"Filter to one skill.","schema":{"type":"string","format":"uuid","description":"Filter to one skill."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: updated_at. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["updated_at","-updated_at"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SkillsScoresItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/skills/self-assessments":{"get":{"summary":"Read the learner's self-assessments","description":"Every competency in the organisation with the learner's own current level and notes (null until assessed), the previous level, the level the learner's role expects, and the level definitions.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["skills"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SkillsSelf-assessmentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"put":{"summary":"Save the learner's self-assessments","description":"Saves one self-assessment (`competency_id`, `self_level`, `notes`) or up to 50 in `assessments`. Every item is validated before any is saved. A level outside 0..max_level, or a competency that is not in this organisation, answers 400. The saves themselves are independent (a database failure part-way leaves the earlier items saved); saving an item is idempotent, so retry the whole call.\n\nRequires scope `module.skills` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["skills"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SkillsSelf-assessmentsPutItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"competency_id":{"type":["string","null"],"format":"uuid","description":"Competency to rate (single form)."},"self_level":{"type":["integer","null"],"description":"Level, 0 to max_level (single form)."},"notes":{"type":["string","null"],"description":"Optional notes, up to 2000 characters."},"assessments":{"type":["array","null"],"description":"Batch form: array of { competency_id, self_level, notes? }."}},"required":[]}}}}}},"/api/v1/skills/skills":{"get":{"summary":"List skills","description":"The skill catalogue for the caller's tenant.\n\nRequires scope `module.skills` and permission `report.view.tenant` (see \"Auth\" in the module document's description).","tags":["skills"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring match against name.","schema":{"type":"string","description":"Case-insensitive substring match against name."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"sort","in":"query","required":false,"description":"Sort column, optionally prefixed with \"-\" for descending. One of: created_at, name. An unrecognised value silently falls back to the default rather than erroring.","schema":{"type":"string","enum":["created_at","name","-created_at","-name"]}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SkillsSkillsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/social/course-discussions":{"get":{"summary":"List course discussion threads","description":"The Q&A threads for a course, nested, pinned first then newest. `scope=lesson` with `lesson_id` returns only that lesson's threads; otherwise the course-level ones. One page; `meta.cursor.next` is always null.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":true,"description":"The course.","schema":{"type":"string","format":"uuid","description":"The course."}},{"name":"scope","in":"query","required":false,"description":"Default `course`.","schema":{"type":"string","enum":["course","lesson"],"description":"Default `course`."}},{"name":"lesson_id","in":"query","required":false,"description":"With `scope=lesson`.","schema":{"type":"string","format":"uuid","description":"With `scope=lesson`."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialCourse-discussionsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Start or reply to a course discussion","description":"Starts a thread, or replies when `parent_id` is given. `course_id` goes in the query string. 404 when the learner cannot see the course. Returns the new node.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"course_id","in":"query","required":true,"description":"The course.","schema":{"type":"string","format":"uuid","description":"The course."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialCourse-discussionsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"Text."},"lesson_id":{"type":["string","null"],"format":"uuid","description":"Attach the thread to a lesson."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The discussion being replied to."}},"required":["body"]}}}}}},"/api/v1/social/feed":{"get":{"summary":"The learner's feed","description":"Posts the learner can see, hot-ranked or newest first, with the learner's own reaction and save on each. `audience=department|org` reproduces the two feed tabs; `all` (default) is every audience the learner can see; `audience=space` with `space_id` is one space's feed. Hot ranks the 200 most-liked visible posts (7-day decay); use `sort=latest` to read history.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"sort","in":"query","required":false,"description":"Ranking. Default `hot`.","schema":{"type":"string","enum":["hot","latest"],"description":"Ranking. Default `hot`."}},{"name":"audience","in":"query","required":false,"description":"Which audience bucket. Default `all`.","schema":{"type":"string","enum":["all","department","org","space"],"description":"Which audience bucket. Default `all`."}},{"name":"space_id","in":"query","required":false,"description":"Required with `audience=space`.","schema":{"type":"string","format":"uuid","description":"Required with `audience=space`."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialFeedItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/social/moderation/{postId}":{"post":{"summary":"Moderate a post","description":"Applies a moderation action to a post. `reason` is required and recorded against the acting person. `report_id` links the action to a report. Optional `content_type` (`post_comment` or `course_discussion`) makes `{postId}` the id of that content instead.\n\nRequires scope `module.community` and permission `community.moderate` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"postId","in":"path","required":true,"description":"Post id (or the id of the content named by `content_type`).","schema":{"type":"string","format":"uuid","description":"Post id (or the id of the content named by `content_type`)."}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialModerationPostIdItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["hide","unhide","lock","unlock","pin","unpin"],"description":"What to do."},"reason":{"type":"string","description":"Why. Required."},"report_id":{"type":["string","null"],"format":"uuid","description":"The report this action resolves."},"content_type":{"type":["string","null"],"enum":["post","post_comment","course_discussion"],"description":"Default `post`."}},"required":["action","reason"]}}}}}},"/api/v1/social/moderation/queue":{"get":{"summary":"Moderation queue","description":"Reports against posts, comments and course discussions, newest first, each with an excerpt of the reported content and how many open reports it has. `status` filters (default `open`). At most 200 reports; `meta.cursor.next` is always null.\n\nRequires scope `module.community` and permission `community.moderate` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"description":"Default `open`.","schema":{"type":"string","enum":["open","upheld","dismissed","all"],"description":"Default `open`."}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialModerationQueueItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/social/posts":{"get":{"summary":"List posts","description":"Published posts the learner can see, newest first. `author_id=me` (or a user id) lists one author's posts; `saved=true` lists the posts the learner saved. With neither filter this is every visible post, newest first. Use `/social/feed` for the hot-ranked feed.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"author_id","in":"query","required":false,"description":"`me` for the learner's own posts, or an author's user id.","schema":{"type":"string","description":"`me` for the learner's own posts, or an author's user id."}},{"name":"saved","in":"query","required":false,"description":"true: only the posts the learner saved (the 500 most recently saved).","schema":{"type":"boolean","description":"true: only the posts the learner saved (the 500 most recently saved)."}},{"name":"space_id","in":"query","required":false,"description":"Only posts in this space.","schema":{"type":"string","format":"uuid","description":"Only posts in this space."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialPostsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Publish a post","description":"Publishes a post as the learner. `audience` picks who sees it: `org` (everyone), `department` (with `department_ids`), `group` (with `group_ids`) or `space` (with `space_id`; the learner must be a member of an active space, else 403). A `video_demo` may carry only a `media_url`. Up to 10 tags; names are lower-cased and hyphenated. Returns the post.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"audience":{"type":"string","enum":["org","department","group","space"],"description":"Who can see the post."},"body":{"type":["string","null"],"description":"Post text. Required unless this is a `video_demo` with a `media_url`."},"title":{"type":["string","null"],"description":"Optional title."},"type":{"type":["string","null"],"enum":["idea","post","video_demo","announcement"],"description":"Default `post`."},"media_url":{"type":["string","null"],"description":"Video link, for a `video_demo`."},"department_ids":{"type":["array","null"],"description":"Department ids, for `audience=department`."},"group_ids":{"type":["array","null"],"description":"Group ids, for `audience=group`."},"space_id":{"type":["string","null"],"format":"uuid","description":"Space id, for `audience=space`."},"tags":{"type":["array","null"],"description":"Tag names."},"is_feedback_requested":{"type":["boolean","null"],"description":"Ask for feedback."}},"required":["audience"]}}}}}},"/api/v1/social/posts/{id}":{"delete":{"summary":"Delete a post","description":"Deletes the learner's own post, with its comments and reactions. 404 when there is no such post or it is not theirs.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"get":{"summary":"Get one post","description":"One post as the learner may see it, with their own reaction and save. A post the learner cannot see answers 404.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdGetItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Edit a post","description":"Edits the learner's own post: any of `title`, `body`, `media_url`, `is_feedback_requested`. Returns the post.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":["string","null"],"description":"New title."},"body":{"type":["string","null"],"description":"New text (not empty)."},"media_url":{"type":["string","null"],"description":"New video link; null clears it."},"is_feedback_requested":{"type":["boolean","null"],"description":"Ask for (or stop asking for) feedback."}},"required":[]}}}}}},"/api/v1/social/posts/{id}/comments":{"get":{"summary":"List comments on a post","description":"Comments and replies on a post, oldest first, flat. A comment a moderator hid is returned only to its author and to moderators.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialPostsIdCommentsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Comment on a post","description":"Adds a comment, or a reply when `parent_id` is given. 409 `conflict` when a moderator locked the thread. Returns the comment.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdCommentsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"Comment text."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The comment being replied to."}},"required":["body"]}}}}}},"/api/v1/social/posts/{id}/comments/{commentId}":{"delete":{"summary":"Delete a comment","description":"Deletes the learner's own comment and its replies.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"commentId","in":"path","required":true,"description":"Comment id.","schema":{"type":"string","format":"uuid","description":"Comment id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdCommentsCommentIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"patch":{"summary":"Edit a comment","description":"Edits the learner's own comment. Returns the comment.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"commentId","in":"path","required":true,"description":"Comment id.","schema":{"type":"string","format":"uuid","description":"Comment id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdCommentsCommentIdPatchItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","description":"New comment text."}},"required":["body"]}}}}}},"/api/v1/social/posts/{id}/reactions":{"delete":{"summary":"Remove a reaction","description":"Removes the learner's reaction: the type given in `?type=` (or the JSON body), or whichever they hold when none is given. Idempotent.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"type","in":"query","required":false,"description":"Only remove this reaction.","schema":{"type":"string","enum":["like","clap","insightful","question"],"description":"Only remove this reaction."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdReactionsItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"React to a post","description":"Sets the learner's reaction on the post, replacing any other reaction they held. Idempotent.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdReactionsItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["like","clap","insightful","question"],"description":"The reaction."}},"required":["type"]}}}}}},"/api/v1/social/posts/{id}/report":{"post":{"summary":"Report a post","description":"Files a report against the post for the moderation queue. `reason` is one of spam, harassment, inappropriate, off_topic, misinformation, other. Repeating it returns the learner's existing open report (`created: false`).\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdReportItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["spam","harassment","inappropriate","off_topic","misinformation","other"],"description":"Why it is being reported."},"detail":{"type":["string","null"],"description":"Optional explanation for the moderator."}},"required":["reason"]}}}}}},"/api/v1/social/posts/{id}/saves":{"delete":{"summary":"Unsave a post","description":"Removes the learner's bookmark. Idempotent.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdSavesItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Save a post","description":"Saves the post for the learner. Idempotent. 404 when the learner cannot see the post.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Post id.","schema":{"type":"string","format":"uuid","description":"Post id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialPostsIdSavesItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/social/spaces":{"get":{"summary":"List spaces","description":"Every space in the organisation with its member count and what the learner may do with it (join, manage). One page; `meta.cursor.next` is always null.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}},{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialSpacesItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/social/spaces/{id}":{"get":{"summary":"Get one space","description":"One space with the learner's role in it and what they may do with it.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Space id.","schema":{"type":"string","format":"uuid","description":"Space id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialSpacesIdItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/api/v1/social/spaces/{id}/members":{"delete":{"summary":"Leave a space, or remove a member","description":"Removes the learner from the space, or the user named in `?user_id=` (allowed only to a space owner, a space moderator or a community moderator). 403 when nothing was removed because the learner may not.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Space id.","schema":{"type":"string","format":"uuid","description":"Space id."}},{"name":"user_id","in":"query","required":false,"description":"Remove this member instead of the learner.","schema":{"type":"string","format":"uuid","description":"Remove this member instead of the learner."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialSpacesIdMembersItem"}},"required":["data"]}}}},"400":{"description":"`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"summary":"Join a space, or add a member","description":"With no body the learner joins the space. `user_id` adds that person instead (the database allows this only to a space owner, a space moderator or a community moderator), and `role` is honoured only then: joining yourself is always as a `member`. 403 for a closed space; 409 when already a member.\n\nRequires scope `module.community` and permission `course.view` (see \"Auth\" in the module document's description).","tags":["social"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Space id.","schema":{"type":"string","format":"uuid","description":"Space id."}},{"name":"X-Learner-Id","in":"header","required":true,"description":"The learner this call acts as: the partner's external_id, or a user_id, inside the key's tenant. The call runs through the learner's own row-level security.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"The resource.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialSpacesIdMembersPostItem"}},"required":["data"]}}}},"400":{"description":"`bad_request` — The request was malformed — an unparseable body, or a parameter whose value is not of the shape the endpoint accepts. Adds: `issues`.\n\n`learner_header_required` — this route needs the `X-Learner-Id` header.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"`unauthorized` — No `Authorization: Bearer <key>` header, or a key that does not resolve — unknown, revoked, or past its expiry.\n\n`key_revoked` — The key exists but was switched off by deprovisioning: the person it acts as has been deactivated in the organisation (which revokes every key acting as them), or the organisation itself is suspended or deleted. A key revoked by hand answers `unauthorized`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"`scope_not_granted` — The key resolved, but it is not scoped for the module this endpoint belongs to. Adds: `scope`.\n\n`module_not_entitled` — The organisation's plan does not currently include this module. Checked live on every request against entitlements, never against a flag stored on the key — so a plan change takes effect on the next call, not when the key is next reissued. Adds: `module`.\n\n`permission_denied` — The principal the key acts as does not hold the permission this endpoint requires. Recomputed from that principal's current roles on every request — a key can never outlive its owner's authority. Adds: `required_permission`.\n\n`product_off` — The product this endpoint belongs to is switched off for this organisation by its partner. Checked live on every request, so a switch takes effect on the next call. The product's data is kept, read-only, and comes back when the product is switched on again. Adds: `product`, `module`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"`not_found` — No resource with that id in this organisation. A resource that exists but belongs to a different organisation is reported this way too, deliberately: a 403 would confirm the id exists.\n\n`learner_not_in_tenant` — the `X-Learner-Id` value does not resolve to an active member of the key's tenant.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"`rate_limited` — Too many requests for this API key in the current window. Requests refused earlier in the chain (unauthenticated, out of scope, unentitled, lacking the permission) do not consume quota. Adds: `retry_after_seconds`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}},"Retry-After":{"description":"RFC 9110. Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"`api_keys_not_provisioned` — API key infrastructure is not present on this server. This is a deployment state, not a fact about your key — no request can succeed until it is fixed.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":["string","null"],"format":"uuid","description":"Add this user instead of the learner."},"role":{"type":["string","null"],"enum":["member","mod","owner"],"description":"Role for an added member. Default `member`."}},"required":[]}}}}}},"/api/v1/versions":{"get":{"summary":"List API versions and their lifecycle","description":"Every published API version with its status (current, deprecated or sunset), the dates attached to it and, for a retired version, the one that replaced it. While a version is deprecated every response from it carries RFC 8594 Deprecation and Sunset headers; once it is sunset every request answers 410 Gone.\n\nNo API key required — this endpoint exists to be readable before you have one.","tags":["meta"],"security":[],"parameters":[{"name":"cursor","in":"query","required":false,"description":"Opaque pagination cursor from a previous response's `meta.cursor.next`.","schema":{"type":"string"}},{"name":"page_size","in":"query","required":false,"description":"Rows per page. Default 25, max 100.","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"A page of results.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VersionsItem"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}},"required":["data"]}}}},"410":{"description":"`version_sunset` — This API version has been retired and no longer answers. The body names the version that replaced it and the date this one stopped. Adds: `version`, `sunset_on`, `replacement_version`, `documentation_url`.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"500":{"description":"`internal_error` — Something failed on our side. The response body never carries the underlying detail.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"503":{"description":"The version catalogue could not be read. Deliberately not an empty list: \"nothing is deprecated\" and \"I cannot tell you\" are different answers.","headers":{"X-API-Version":{"description":"The API version that answered this request.","schema":{"type":"string","examples":["v1"]}},"RateLimit-Limit":{"description":"Requests permitted for this key in the current window. Absent when the limiter could not be consulted — never a guessed number.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets. A delta, not a timestamp, so a client with a wrong clock can still use it.","schema":{"type":"integer"}},"RateLimit-Policy":{"description":"The published policy, as `<limit>;w=<window seconds>`.","schema":{"type":"string","examples":["60;w=60"]}},"Deprecation":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day deprecation began.","schema":{"type":"string"}},"Sunset":{"description":"RFC 8594. Present only while this version is deprecated; an HTTP-date giving the day it stops answering.","schema":{"type":"string"}},"Link":{"description":"Present only while this version is deprecated: `rel=\"deprecation\"` pointing at the policy, and `rel=\"successor-version\"` when a replacement is named.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}},"components":{"schemas":{"ErrorEnvelope":{"type":"object","description":"Every non-2xx response from /api/v1. `code` is the machine-readable discriminant to branch on; `error` is for humans. Some errors add extra fields (e.g. `retry_after_seconds` on 429, `required_permission` on 403). The full catalogue, including which codes are worth retrying, is published at /developers#errors.","properties":{"error":{"type":"string","description":"Human-readable message."},"code":{"type":"string","description":"Machine-readable error code.","enum":["bad_request","payload_too_large","unauthorized","key_revoked","scope_not_granted","module_not_entitled","permission_denied","not_found","route_not_found","version_sunset","rate_limited","internal_error","api_versions_not_provisioned","api_keys_not_provisioned","feature_not_provisioned","partner_scope_required","partner_role_insufficient","partner_suspended","tenant_not_child","partner_child_limit","wrong_region","module_outside_contract","partner_tier_above_contract","partner_tier_free","trial_on_partner_child","unknown_tier","partner_exiting","learner_header_required","learner_not_in_tenant","origin_not_allowed","target_not_embeddable","email_not_allowed","email_in_use","learner_cap","learner_limit_reached","limit_reached","legal_hold","conflict","validation_failed","ai_quota_exceeded","allowance_exhausted","insufficient_credits","guardrails_missing","format_unavailable","feature_disabled","ai_output_invalid","completion_required","idempotency_conflict","lesson_locked","proctored_requires_top_level","too_large","topups_not_applicable","payment_account_required","payment_provider_error","partner_subscription_not_available","ai_refused","product_off","unknown_product","product_requires","product_outside_contract","products_required","sandbox_customer_cap","sandbox_user_cap","sandbox_credit_cap","test_key_required","test_key_on_live_tenant","test_customer_immutable"]}},"required":["error","code"]},"CursorMeta":{"type":"object","properties":{"cursor":{"type":"object","properties":{"next":{"type":["string","null"],"description":"Opaque cursor for the next page, or null on the last page. Pass back as `?cursor=`."}},"required":["next"]}}},"AdminAudit-eventsItem":{"type":"object","properties":{"id":{"type":"integer","description":"Event id (monotonic)."},"created_at":{"type":"string","format":"date-time","description":"When it happened."},"actor_id":{"type":["string","null"],"format":"uuid","description":"Who did it (null for the system)."},"action":{"type":"string","description":"What happened, e.g. `group.created`."},"target_type":{"type":"string","description":"Kind of record affected."},"target_id":{"type":"string","description":"Which record."},"before":{"type":["object","null"],"description":"State before, when recorded."},"after":{"type":["object","null"],"description":"State after, when recorded."},"meta":{"type":"object","description":"Extra context, including `api_key_id` for API writes."},"ip":{"type":["string","null"],"description":"Source address when known."}},"required":["id","created_at","actor_id","action","target_type","target_id","before","after","meta","ip"]},"AdminAutomation-rulesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Rule id."},"name":{"type":"string","description":"Rule name."},"description":{"type":"string","description":"What it is for."},"trigger":{"type":"object","description":"The event that fires it, e.g. { \"type\": \"group.joined\", \"group_id\": \"...\" }."},"action":{"type":"object","description":"What it does, e.g. { \"type\": \"assign_course\", \"course_id\": \"...\" }."},"is_active":{"type":"boolean","description":"Whether it is running."},"created_by":{"type":["string","null"],"format":"uuid","description":"Who created it."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","name","description","trigger","action","is_active","created_by","created_at","updated_at"]},"AdminAutomation-rulesIdItem":{"type":"object","properties":{"deleted":{"type":"boolean","description":"True when the rule is gone."}},"required":["deleted"]},"AdminAutomation-rulesIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Rule id."},"name":{"type":"string","description":"Rule name."},"description":{"type":"string","description":"What it is for."},"trigger":{"type":"object","description":"The firing event."},"action":{"type":"object","description":"What it does."},"is_active":{"type":"boolean","description":"Whether it is running."},"created_by":{"type":["string","null"],"format":"uuid","description":"Who created it."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","name","description","trigger","action","is_active","created_by","created_at","updated_at"]},"AdminAutomation-rulesIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Rule id."},"name":{"type":"string","description":"Rule name."},"description":{"type":"string","description":"What it is for."},"trigger":{"type":"object","description":"The firing event."},"action":{"type":"object","description":"What it does."},"is_active":{"type":"boolean","description":"Whether it is running."},"created_by":{"type":["string","null"],"format":"uuid","description":"Who created it."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","name","description","trigger","action","is_active","created_by","created_at","updated_at"]},"AdminAutomation-rulesIdRunsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Run id."},"event_type":{"type":"string","description":"The event that matched."},"event_payload":{"type":"object","description":"The event."},"chain_depth":{"type":"integer","description":"How many rule firings deep this run was."},"status":{"type":"string","enum":["success","failed","skipped_depth_exceeded","skipped_already_visited"],"description":"Outcome of the firing."},"detail":{"type":["string","null"],"description":"Why it was skipped or failed."},"ran_at":{"type":"string","format":"date-time","description":"When."}},"required":["id","event_type","event_payload","chain_depth","status","detail","ran_at"]},"AdminChannelsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Channel id."},"type":{"type":"string","enum":["slack","teams","google_chat"],"description":"slack, teams or google_chat."},"name":{"type":"string","description":"Display name."},"url_hint":{"type":"string","description":"The last characters of the webhook URL. The URL itself is never returned."},"is_active":{"type":"boolean","description":"Whether notifications are sent to it."},"dm_enabled":{"type":"boolean","description":"Whether direct messages through the workspace bot are on."},"bot_token_set":{"type":"boolean","description":"A Slack bot token is stored (never returned)."},"app_secret_set":{"type":"boolean","description":"A Teams app secret is stored (never returned)."},"app_id":{"type":["string","null"],"description":"Teams app id."},"azure_tenant_id":{"type":["string","null"],"description":"Teams Azure tenant id."},"last_status":{"type":["string","null"],"enum":["success","failed"],"description":"Outcome of the last post."},"last_error":{"type":["string","null"],"description":"Why the last post failed."},"last_used_at":{"type":["string","null"],"format":"date-time","description":"When it last posted."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","type","name","url_hint","is_active","dm_enabled","bot_token_set","app_secret_set","app_id","azure_tenant_id","last_status","last_error","last_used_at","created_at","updated_at"]},"AdminChannelsIdItem":{"type":"object","properties":{"deleted":{"type":"boolean","description":"True when the channel is gone."}},"required":["deleted"]},"AdminChannelsIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Channel id."},"type":{"type":"string","enum":["slack","teams","google_chat"],"description":"slack, teams or google_chat."},"name":{"type":"string","description":"Display name."},"url_hint":{"type":"string","description":"The last characters of the webhook URL."},"is_active":{"type":"boolean","description":"Whether notifications are sent to it."},"dm_enabled":{"type":"boolean","description":"Direct messages through the workspace bot."},"bot_token_set":{"type":"boolean","description":"A Slack bot token is stored."},"app_secret_set":{"type":"boolean","description":"A Teams app secret is stored."},"app_id":{"type":["string","null"],"description":"Teams app id."},"azure_tenant_id":{"type":["string","null"],"description":"Teams Azure tenant id."},"last_status":{"type":["string","null"],"enum":["success","failed"],"description":"Outcome of the last post."},"last_error":{"type":["string","null"],"description":"Why the last post failed."},"last_used_at":{"type":["string","null"],"format":"date-time","description":"When it last posted."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","type","name","url_hint","is_active","dm_enabled","bot_token_set","app_secret_set","app_id","azure_tenant_id","last_status","last_error","last_used_at","created_at","updated_at"]},"AdminChannelsIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Channel id."},"type":{"type":"string","enum":["slack","teams","google_chat"],"description":"slack, teams or google_chat."},"name":{"type":"string","description":"Display name."},"url_hint":{"type":"string","description":"The last characters of the webhook URL."},"is_active":{"type":"boolean","description":"Whether notifications are sent to it."},"dm_enabled":{"type":"boolean","description":"Direct messages through the workspace bot."},"bot_token_set":{"type":"boolean","description":"A Slack bot token is stored."},"app_secret_set":{"type":"boolean","description":"A Teams app secret is stored."},"app_id":{"type":["string","null"],"description":"Teams app id."},"azure_tenant_id":{"type":["string","null"],"description":"Teams Azure tenant id."},"last_status":{"type":["string","null"],"enum":["success","failed"],"description":"Outcome of the last post."},"last_error":{"type":["string","null"],"description":"Why the last post failed."},"last_used_at":{"type":["string","null"],"format":"date-time","description":"When it last posted."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","type","name","url_hint","is_active","dm_enabled","bot_token_set","app_secret_set","app_id","azure_tenant_id","last_status","last_error","last_used_at","created_at","updated_at"]},"AdminChannelsIdTestItem":{"type":"object","properties":{"ok":{"type":"boolean","description":"True when the workspace accepted the message."}},"required":["ok"]},"AdminDepartmentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Department id."},"name":{"type":"string","description":"Name."},"slug":{"type":"string","description":"URL-safe name."},"description":{"type":["string","null"],"description":"Description."},"parent_id":{"type":["string","null"],"format":"uuid","description":"Parent node; null for a root."},"type":{"type":"string","enum":["vertical","department","team","location"],"description":"Level label: vertical, department, team or location."},"color":{"type":["string","null"],"description":"Display colour."},"created_at":{"type":"string","format":"date-time","description":"Created."}},"required":["id","name","slug","description","parent_id","type","color","created_at"]},"AdminDepartmentsIdItem":{"type":"object","properties":{"deleted":{"type":"boolean","description":"True when the node is gone."}},"required":["deleted"]},"AdminDepartmentsIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Department id."},"name":{"type":"string","description":"Name."},"slug":{"type":"string","description":"URL-safe name."},"description":{"type":["string","null"],"description":"Description."},"parent_id":{"type":["string","null"],"format":"uuid","description":"Parent node."},"type":{"type":"string","enum":["vertical","department","team","location"],"description":"Level label."},"color":{"type":["string","null"],"description":"Display colour."},"created_at":{"type":"string","format":"date-time","description":"Created."}},"required":["id","name","slug","description","parent_id","type","color","created_at"]},"AdminDepartmentsIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Department id."},"name":{"type":"string","description":"Name."},"slug":{"type":"string","description":"URL-safe name."},"description":{"type":["string","null"],"description":"Description."},"parent_id":{"type":["string","null"],"format":"uuid","description":"Parent node."},"type":{"type":"string","enum":["vertical","department","team","location"],"description":"Level label."},"color":{"type":["string","null"],"description":"Display colour."},"created_at":{"type":"string","format":"date-time","description":"Created."}},"required":["id","name","slug","description","parent_id","type","color","created_at"]},"AdminEmail-templatesItem":{"type":"object","properties":{"kind":{"type":"string","description":"The template kind (the id of the resource)."},"label":{"type":"string","description":"Human name of the kind."},"subject":{"type":"string","description":"Current subject (override, else default)."},"body_markdown":{"type":"string","description":"Current body (override, else default)."},"is_custom":{"type":"boolean","description":"True when the organisation has overridden the default."},"updated_at":{"type":["string","null"],"format":"date-time","description":"When the override last changed."},"default_subject":{"type":"string","description":"The built-in subject."},"default_body_markdown":{"type":"string","description":"The built-in body."}},"required":["kind","label","subject","body_markdown","is_custom","updated_at","default_subject","default_body_markdown"]},"AdminEmail-templatesKindItem":{"type":"object","properties":{"kind":{"type":"string","description":"The template kind."},"label":{"type":"string","description":"Human name of the kind."},"subject":{"type":"string","description":"Current subject."},"body_markdown":{"type":"string","description":"Current body."},"is_custom":{"type":"boolean","description":"True when overridden."},"updated_at":{"type":["string","null"],"format":"date-time","description":"Override last changed."},"default_subject":{"type":"string","description":"The built-in subject."},"default_body_markdown":{"type":"string","description":"The built-in body."}},"required":["kind","label","subject","body_markdown","is_custom","updated_at","default_subject","default_body_markdown"]},"AdminEmail-templatesKindTest-sendItem":{"type":"object","properties":{"ok":{"type":"boolean","description":"True when the send was accepted."},"to":{"type":"string","description":"The address it went to (the administrator the key acts as)."},"delivered":{"type":"boolean","description":"False when the platform is not configured to deliver (the send was recorded only)."},"from_name":{"type":["string","null"],"description":"Sender display name recipients see."},"reply_to":{"type":["string","null"],"description":"Reply-to address."}},"required":["ok","to","delivered","from_name","reply_to"]},"AdminGroupsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Group id."},"name":{"type":"string","description":"Group name."},"description":{"type":"string","description":"Description."},"is_dynamic":{"type":"boolean","description":"True when membership is computed from `rule`."},"rule":{"type":["object","null"],"description":"Structured membership rule for a dynamic group."},"owner_department_id":{"type":["string","null"],"format":"uuid","description":"Department the group belongs to (scopes a delegated administrator)."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","name","description","is_dynamic","rule","owner_department_id","created_at","updated_at"]},"AdminGroupsIdItem":{"type":"object","properties":{"deleted":{"type":"boolean","description":"True when the group is gone."}},"required":["deleted"]},"AdminGroupsIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Group id."},"name":{"type":"string","description":"Group name."},"description":{"type":"string","description":"Description."},"is_dynamic":{"type":"boolean","description":"True when membership is computed from `rule`."},"rule":{"type":["object","null"],"description":"Structured membership rule."},"owner_department_id":{"type":["string","null"],"format":"uuid","description":"Owning department."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","name","description","is_dynamic","rule","owner_department_id","created_at","updated_at"]},"AdminGroupsIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Group id."},"name":{"type":"string","description":"Group name."},"description":{"type":"string","description":"Description."},"is_dynamic":{"type":"boolean","description":"True when membership is computed from `rule`."},"rule":{"type":["object","null"],"description":"Structured membership rule."},"owner_department_id":{"type":["string","null"],"format":"uuid","description":"Owning department."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","name","description","is_dynamic","rule","owner_department_id","created_at","updated_at"]},"AdminGroupsIdMembersItem":{"type":"object","properties":{"removed":{"type":"integer","description":"How many ids were asked to be removed."}},"required":["removed"]},"AdminGroupsIdMembersGetItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Member."},"full_name":{"type":["string","null"],"description":"Display name."},"department_id":{"type":["string","null"],"format":"uuid","description":"Department."},"job_title_id":{"type":["string","null"],"format":"uuid","description":"Job title."}},"required":["user_id","full_name","department_id","job_title_id"]},"AdminGroupsIdMembersPostItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Person."},"ok":{"type":"boolean","description":"Whether they were added."},"reason":{"type":["string","null"],"description":"Why not."}},"required":["user_id","ok","reason"]},"AdminLegal-holdsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Hold id."},"subject_type":{"type":"string","enum":["user","course"],"description":"What is held."},"subject_id":{"type":"string","format":"uuid","description":"The person or course."},"reason":{"type":"string","description":"Why the hold was placed."},"placed_by":{"type":["string","null"],"format":"uuid","description":"Who placed it."},"placed_at":{"type":"string","format":"date-time","description":"When."},"released_by":{"type":["string","null"],"format":"uuid","description":"Who released it."},"released_at":{"type":["string","null"],"format":"date-time","description":"When it was released; null while open."},"released_reason":{"type":["string","null"],"description":"Why it was released."}},"required":["id","subject_type","subject_id","reason","placed_by","placed_at","released_by","released_at","released_reason"]},"AdminLegal-holdsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Hold id."},"released_at":{"type":["string","null"],"format":"date-time","description":"When it was released."},"released_reason":{"type":["string","null"],"description":"Why."}},"required":["id","released_at","released_reason"]},"AdminPeopleItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"User id."},"email":{"type":"string","description":"Sign-in email (a non-routable address for a pseudonymous learner)."},"full_name":{"type":["string","null"],"description":"Display name."},"title":{"type":["string","null"],"description":"Free-text job title."},"department_id":{"type":["string","null"],"format":"uuid","description":"Department."},"department_name":{"type":["string","null"],"description":"Department name."},"manager_id":{"type":["string","null"],"format":"uuid","description":"Line manager (user id)."},"audience_type":{"type":"string","description":"employee, contractor, customer, partner, public or buyer."},"external_id":{"type":["string","null"],"description":"The partner system's identifier for this person."},"is_active":{"type":"boolean","description":"Always true on this list: deactivated people are not listed."},"created_at":{"type":"string","format":"date-time","description":"When the person joined."}},"required":["id","email","full_name","title","department_id","department_name","manager_id","audience_type","external_id","is_active","created_at"]},"AdminPeopleIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"User id."},"email":{"type":["string","null"],"description":"Sign-in email."},"full_name":{"type":["string","null"],"description":"Display name."},"title":{"type":["string","null"],"description":"Free-text job title."},"job_title_id":{"type":["string","null"],"format":"uuid","description":"Job title record."},"department_id":{"type":["string","null"],"format":"uuid","description":"Department."},"location_id":{"type":["string","null"],"format":"uuid","description":"Office."},"manager_id":{"type":["string","null"],"format":"uuid","description":"Line manager."},"audience_type":{"type":"string","description":"employee, contractor, customer, partner, public or buyer."},"external_id":{"type":["string","null"],"description":"The partner system's identifier."},"is_active":{"type":"boolean","description":"Whether the person can sign in to this organisation."},"roles":{"type":"array","description":"Role grants: { role_key, role_name, scope_department_id }."},"created_at":{"type":["string","null"],"format":"date-time","description":"Profile creation time."}},"required":["id","email","full_name","title","job_title_id","department_id","location_id","manager_id","audience_type","external_id","is_active","roles","created_at"]},"AdminReport-schedulesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Schedule id."},"name":{"type":"string","description":"Schedule name."},"report_request":{"type":"object","description":"The report to run: { subject, columns?, filters?, sort?, limit? }."},"format":{"type":"string","enum":["csv","pdf"],"description":"Attachment format."},"cadence":{"type":"string","enum":["daily","weekly","monthly"],"description":"How often."},"send_hour_utc":{"type":"integer","description":"Hour of day (UTC) it is sent."},"weekday":{"type":["integer","null"],"description":"0 (Sunday) to 6, for weekly."},"day_of_month":{"type":["integer","null"],"description":"1 to 28, for monthly."},"recipients":{"type":"array","description":"Up to 25 email addresses."},"is_active":{"type":"boolean","description":"Whether it is running."},"last_run_at":{"type":["string","null"],"format":"date-time","description":"When it last ran."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","name","report_request","format","cadence","send_hour_utc","weekday","day_of_month","recipients","is_active","last_run_at","created_at","updated_at"]},"AdminReport-schedulesIdItem":{"type":"object","properties":{"deleted":{"type":"boolean","description":"True when the schedule is gone."}},"required":["deleted"]},"AdminReport-schedulesIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Schedule id."},"name":{"type":"string","description":"Schedule name."},"report_request":{"type":"object","description":"The report to run."},"format":{"type":"string","enum":["csv","pdf"],"description":"Attachment format."},"cadence":{"type":"string","enum":["daily","weekly","monthly"],"description":"How often."},"send_hour_utc":{"type":"integer","description":"Hour (UTC)."},"weekday":{"type":["integer","null"],"description":"Weekly: 0 (Sunday) to 6."},"day_of_month":{"type":["integer","null"],"description":"Monthly: 1 to 28."},"recipients":{"type":"array","description":"Email addresses."},"is_active":{"type":"boolean","description":"Whether it is running."},"last_run_at":{"type":["string","null"],"format":"date-time","description":"When it last ran."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."},"deliveries":{"type":"array","description":"GET only: { id, started_at, status, recipient_count, row_count, error_message }."}},"required":["id","name","report_request","format","cadence","send_hour_utc","weekday","day_of_month","recipients","is_active","last_run_at","created_at","updated_at","deliveries"]},"AdminReport-schedulesIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Schedule id."},"name":{"type":"string","description":"Schedule name."},"report_request":{"type":"object","description":"The report to run."},"format":{"type":"string","enum":["csv","pdf"],"description":"Attachment format."},"cadence":{"type":"string","enum":["daily","weekly","monthly"],"description":"How often."},"send_hour_utc":{"type":"integer","description":"Hour (UTC)."},"weekday":{"type":["integer","null"],"description":"Weekly: 0 (Sunday) to 6."},"day_of_month":{"type":["integer","null"],"description":"Monthly: 1 to 28."},"recipients":{"type":"array","description":"Email addresses."},"is_active":{"type":"boolean","description":"Whether it is running."},"last_run_at":{"type":["string","null"],"format":"date-time","description":"When it last ran."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."},"deliveries":{"type":"array","description":"GET only: { id, started_at, status, recipient_count, row_count, error_message }."}},"required":["id","name","report_request","format","cadence","send_hour_utc","weekday","day_of_month","recipients","is_active","last_run_at","created_at","updated_at","deliveries"]},"AdminReportsItem":{"type":"object","properties":{"catalogue":{"type":"array","description":"Subjects: { key, label, description, columns: [{ key, label, type, filterable }] }."},"saved":{"type":"array","description":"Saved reports: { id, name, description, report_request, created_at, updated_at }."}},"required":["catalogue","saved"]},"AdminReportsRunItem":{"type":"object","properties":{"report_key":{"type":"string","description":"The subject that ran."},"columns":{"type":"array","description":"Column keys, in row order."},"rows":{"type":"array","description":"The rows."},"total":{"type":"integer","description":"Rows matching, before the page limit."}},"required":["report_key","columns","rows","total"]},"AdminRetention-policiesItem":{"type":"object","properties":{"class_key":{"type":"string","description":"The retention class."},"retention_days":{"type":"integer","description":"The configured window."},"is_default":{"type":"boolean","description":"True when the organisation has not changed the platform default."},"updated_by":{"type":["string","null"],"format":"uuid","description":"Who last changed it."},"updated_at":{"type":["string","null"],"format":"date-time","description":"When it last changed."}},"required":["class_key","retention_days","is_default","updated_by","updated_at"]},"AdminRole-assignmentsItem":{"type":"object","properties":{"revoked":{"type":"boolean","description":"True when the grant is gone."}},"required":["revoked"]},"AdminRole-assignmentsGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Grant id."},"user_id":{"type":"string","format":"uuid","description":"Person holding the role."},"role_key":{"type":"string","description":"Role key (see /roles)."},"role_name":{"type":["string","null"],"description":"Role display name."},"scope_department_id":{"type":["string","null"],"format":"uuid","description":"When set, the grant applies only within this department and its descendants."},"granted_at":{"type":"string","format":"date-time","description":"When it was granted."}},"required":["id","user_id","role_key","role_name","scope_department_id","granted_at"]},"AdminRole-assignmentsPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Grant id."},"user_id":{"type":"string","format":"uuid","description":"Person holding the role."},"role_key":{"type":"string","description":"Role key (see /roles)."},"role_name":{"type":["string","null"],"description":"Role display name."},"scope_department_id":{"type":["string","null"],"format":"uuid","description":"When set, the grant applies only within this department and its descendants."},"granted_at":{"type":"string","format":"date-time","description":"When it was granted."}},"required":["id","user_id","role_key","role_name","scope_department_id","granted_at"]},"AdminRolesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Role id."},"key":{"type":"string","description":"Role key: pass as `role_key` to /role-assignments."},"name":{"type":"string","description":"Display name."},"description":{"type":["string","null"],"description":"What the role is for."},"is_system":{"type":"boolean","description":"True for a built-in role."},"permission_keys":{"type":"array","description":"has_permission() keys the role carries."},"grants_organisation_admin":{"type":"boolean","description":"True if the role carries tenant.admin. Such a role can never be assigned through the API."},"member_count":{"type":"integer","description":"People holding the role, within your remit."}},"required":["id","key","name","description","is_system","permission_keys","grants_organisation_admin","member_count"]},"AiCoachItem":{"type":"object","properties":{"reply":{"type":"string","description":"The coach's reply."},"disclosure":{"type":["string","null"],"description":"The AI-interaction notice your UI must show (EU AI Act Article 50), or null."}},"required":["reply","disclosure"]},"AiFlashcardsItem":{"type":"object","properties":{"cards":{"type":"array","description":"`[{ front: string, back: string }]`."},"disclosure":{"type":["string","null"],"description":"The AI notice to show, or null."}},"required":["cards","disclosure"]},"AiGradeItem":{"type":"object","properties":{"rubric_scores":{"type":"array","description":"One row per criterion with `ai_suggested_score` and `ai_suggested_rationale`; `human_score` is null until a grader decides."},"disclosure":{"type":["string","null"],"description":"The AI notice to show a grader, or null."}},"required":["rubric_scores","disclosure"]},"AiMentor-checkinItem":{"type":"object","properties":{"reply":{"type":"string","description":"The mentor's reply."},"conversation_id":{"type":["string","null"],"format":"uuid","description":"Pass back to continue the conversation."},"recent_attempts":{"type":"integer","description":"How many recent practice attempts informed the reply."},"disclosure":{"type":["string","null"],"description":"The AI notice to show, or null."}},"required":["reply","conversation_id","recent_attempts","disclosure"]},"AiPlanItem":{"type":"object","properties":{"plan":{"type":"object","description":"`{ pathId, title, description, courseIds, disclosure }`."}},"required":["plan"]},"AiQuestionsGenerateItem":{"type":"object","properties":{"questions":{"type":"array","description":"The saved draft rows (id, format, prompt, tags, difficulty, points, config, explanation, status, source, created_at)."},"model":{"type":"string","description":"Model that produced them."},"provider":{"type":"string","description":"Provider that served the model."}},"required":["questions","model","provider"]},"AiReading-companionItem":{"type":"object","properties":{"reply":{"type":"string","description":"The companion's reply."},"reading_level":{"type":"string","description":"The level used."},"disclosure":{"type":["string","null"],"description":"The AI-interaction notice your UI must show, or null."}},"required":["reply","reading_level","disclosure"]},"AiRoleplaySessionsItem":{"type":"object","properties":{"session_id":{"type":"string","format":"uuid","description":"Use in the turns and score endpoints."},"opening_turn":{"type":"object","description":"`{ role: \"assistant\", content }` -- the persona's first line."},"disclosure":{"type":["string","null"],"description":"The AI-interaction notice (EU AI Act Article 50) your UI must show the learner. Null when none is required."}},"required":["session_id","opening_turn","disclosure"]},"AiRoleplaySessionsIdScoreItem":{"type":"object","properties":{"score":{"type":"integer","description":"Score 0-100."},"rubric":{"type":"object","description":"`{ strengths: string[], improvements: string[], objectives_met: number | null }`."},"feedback":{"type":"string","description":"Human-readable summary of the rubric."},"attempt_recorded":{"type":"boolean","description":"False when the score was saved but the attempt row (which drives `roleplay.scored`) could not be written."},"disclosure":{"type":["string","null"],"description":"The AI notice to show, or null (also null on a repeat call)."}},"required":["score","rubric","feedback","attempt_recorded","disclosure"]},"AiRoleplaySessionsIdTurnsItem":{"type":"object","properties":{"reply":{"type":"string","description":"The persona's reply."},"turn_no":{"type":"integer","description":"How many learner messages this session now holds (1-based)."},"disclosure":{"type":["string","null"],"description":"The AI-interaction notice to show, or null."}},"required":["reply","turn_no","disclosure"]},"AiTranslateItem":{"type":"object","properties":{"artifact_id":{"type":"string","format":"uuid","description":"The locale-scoped Studio artifact holding the candidate."},"candidate_revision_id":{"type":"string","format":"uuid","description":"The candidate revision to review."},"base_revision":{"type":"integer","description":"The artifact revision the candidate was generated against."},"job_id":{"type":["string","null"],"description":"Always null: translation is synchronous."},"disclosure":{"type":["string","null"],"description":"The AI notice to show a reviewer, or null."}},"required":["artifact_id","candidate_revision_id","base_revision","job_id","disclosure"]},"AssessAssessmentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Assessment id."},"title":{"type":"string","description":"Title."},"description":{"type":["string","null"],"description":"Description."},"type":{"type":"string","description":"diagnostic, pretest, posttest, formative, summative, practical or virtual_mock."},"course_id":{"type":["string","null"],"format":"uuid","description":"The course it belongs to, if any."},"pretest_assessment_id":{"type":["string","null"],"format":"uuid","description":"For a posttest, the pretest it is paired with so learning gain can be measured. Null otherwise."},"pass_mark":{"type":["integer","null"],"description":"Pass mark in percent. Null means the organisation default applies."},"max_attempts":{"type":"integer","description":"Attempts allowed. 0 means the organisation default applies (itself 0 = unlimited)."},"time_limit_minutes":{"type":["integer","null"],"description":"Time limit. Null means untimed."},"randomize_questions":{"type":"boolean","description":"Whether question order is randomised per attempt."},"allow_answer_review":{"type":"boolean","description":"Whether the learner sees a per-question review after finishing."},"requires_signature":{"type":"boolean","description":"Whether completing it needs an e-signature. Signing is top-level only (needs MFA step-up), see docs/api-v1.md."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Lifecycle state. A learner only ever sees published ones."},"proctored":{"type":"boolean","description":"True when a proctoring policy is on. A proctored attempt cannot be started through the API: open it top-level through an embed session."},"attempts_used":{"type":["integer","null"],"description":"Attempts this learner has used. Null on the administrator variant (no X-Learner-Id header)."},"attempts_remaining":{"type":["integer","null"],"description":"Attempts left, or null when unlimited or on the administrator variant."},"in_progress_attempt_id":{"type":["string","null"],"format":"uuid","description":"This learner's open attempt, if any (resume it with POST /assess/attempts)."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","title","description","type","course_id","pretest_assessment_id","pass_mark","max_attempts","time_limit_minutes","randomize_questions","allow_answer_review","requires_signature","status","proctored","attempts_used","attempts_remaining","in_progress_attempt_id","created_at","updated_at"]},"AssessAssessmentsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Assessment id."},"title":{"type":"string","description":"Title."},"description":{"type":["string","null"],"description":"Description."},"type":{"type":"string","description":"diagnostic, pretest, posttest, formative, summative, practical or virtual_mock."},"course_id":{"type":["string","null"],"format":"uuid","description":"The course it belongs to, if any."},"pretest_assessment_id":{"type":["string","null"],"format":"uuid","description":"For a posttest, the pretest it is paired with so learning gain can be measured. Null otherwise."},"pass_mark":{"type":["integer","null"],"description":"Pass mark in percent. Null means the organisation default applies."},"max_attempts":{"type":"integer","description":"Attempts allowed. 0 means the organisation default applies (itself 0 = unlimited)."},"time_limit_minutes":{"type":["integer","null"],"description":"Time limit. Null means untimed."},"randomize_questions":{"type":"boolean","description":"Whether question order is randomised per attempt."},"allow_answer_review":{"type":"boolean","description":"Whether the learner sees a per-question review after finishing."},"requires_signature":{"type":"boolean","description":"Whether completing it needs an e-signature. Signing is top-level only (needs MFA step-up), see docs/api-v1.md."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Lifecycle state. A learner only ever sees published ones."},"proctored":{"type":"boolean","description":"True when a proctoring policy is on. A proctored attempt cannot be started through the API: open it top-level through an embed session."},"attempts_used":{"type":["integer","null"],"description":"Attempts this learner has used. Null on the administrator variant (no X-Learner-Id header)."},"attempts_remaining":{"type":["integer","null"],"description":"Attempts left, or null when unlimited or on the administrator variant."},"in_progress_attempt_id":{"type":["string","null"],"format":"uuid","description":"This learner's open attempt, if any (resume it with POST /assess/attempts)."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."},"question_count":{"type":["integer","null"],"description":"Number of questions. Administrator variant only; null for a learner."}},"required":["id","title","description","type","course_id","pretest_assessment_id","pass_mark","max_attempts","time_limit_minutes","randomize_questions","allow_answer_review","requires_signature","status","proctored","attempts_used","attempts_remaining","in_progress_attempt_id","created_at","updated_at","question_count"]},"AssessAttemptsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attempt id."},"user_id":{"type":"string","format":"uuid","description":"Learner who attempted."},"lesson_id":{"type":"string","format":"uuid","description":"Lesson containing the quiz block."},"block_id":{"type":"string","format":"uuid","description":"The quiz content block attempted."},"attempt_no":{"type":"integer","description":"Attempt number for this learner/block, starting at 1."},"selected_answer":{"type":"string","description":"The answer the learner selected, as submitted."},"is_correct":{"type":"boolean","description":"Whether the selected answer was correct."},"answered_at":{"type":"string","format":"date-time","description":"When the attempt was recorded."}},"required":["id","user_id","lesson_id","block_id","attempt_no","selected_answer","is_correct","answered_at"]},"AssessAttemptsPostItem":{"type":"object","properties":{"attempt_id":{"type":"string","format":"uuid","description":"Attempt id; use it for responses, submit and result."},"attempt_no":{"type":"integer","description":"The learner's attempt number for this assessment."},"status":{"type":"string","description":"in_progress."},"started_at":{"type":"string","format":"date-time","description":"When the attempt started."},"time_limit_minutes":{"type":["integer","null"],"description":"Time limit, or null when untimed."},"deadline":{"type":["string","null"],"format":"date-time","description":"started_at plus the time limit, or null when untimed. Answers after it are refused (409, reason time_expired)."},"questions":{"type":"array","description":"Question set: { question_id, format, prompt, points, config, position }. Never contains answer keys."}},"required":["attempt_id","attempt_no","status","started_at","time_limit_minutes","deadline","questions"]},"AssessAttemptsIdGradeItem":{"type":"object","properties":{"attempt_id":{"type":"string","format":"uuid","description":"Attempt id."},"finalized":{"type":"boolean","description":"Whether finalize ran and closed or re-evaluated the attempt."},"status":{"type":"string","description":"submitted or graded."},"score":{"type":["integer","null"],"description":"Percent, once graded."},"passed":{"type":["boolean","null"],"description":"Whether the score met the pass mark, once graded."},"awaiting_human_grading":{"type":"integer","description":"Responses still without a grade."},"rubric_scores":{"type":"array","description":"The rows written: { id, response_id, rubric_id, criterion_key, human_score, human_rationale, graded_by, graded_at, ai_suggested_score, ai_suggested_rationale }."}},"required":["attempt_id","finalized","status","score","passed","awaiting_human_grading","rubric_scores"]},"AssessAttemptsIdResponsesItem":{"type":"object","properties":{"recorded":{"type":"boolean","description":"true when the answer was stored."}},"required":["recorded"]},"AssessAttemptsIdResultItem":{"type":"object","properties":{"finalized":{"type":"boolean","description":"true once the attempt is submitted or graded."},"status":{"type":"string","description":"submitted or graded."},"score":{"type":["integer","null"],"description":"Percent, null while a human grade is pending."},"pass_mark":{"type":["integer","null"],"description":"Pass mark in percent."},"passed":{"type":["boolean","null"],"description":"Whether the score met the pass mark."},"assessment_title":{"type":"string","description":"Assessment title."},"summary":{"type":"object","description":"{ total_questions, total_points, earned_points, correct_count, incorrect_count, ungraded_count }."},"review_allowed":{"type":"boolean","description":"Whether `questions` is populated."},"questions":{"type":["array","null"],"description":"Per-question review when allowed, else null."}},"required":["finalized","status","score","pass_mark","passed","assessment_title","summary","review_allowed","questions"]},"AssessAttemptsIdSubmitItem":{"type":"object","properties":{"finalized":{"type":"boolean","description":"true when the attempt is closed."},"status":{"type":"string","enum":["submitted","graded"],"description":"graded, or submitted while a human grade is pending."},"awaiting_human_grading":{"type":"integer","description":"Responses still needing a human grade."},"result":{"type":"object","description":"The learner-visible result: { score, pass_mark, passed, assessment_title, summary, review_allowed, questions }. `questions` is null unless review is allowed."}},"required":["finalized","status","awaiting_human_grading","result"]},"AssessAttemptsIdUpload-urlItem":{"type":"object","properties":{"bucket":{"type":"string","description":"The private storage bucket."},"path":{"type":"string","description":"The object path. Save this as the question's answer."},"upload_url":{"type":"string","description":"Where to PUT the file bytes. Single use, short lived."},"token":{"type":"string","description":"The upload token embedded in `upload_url`, for clients that use a storage SDK."},"file_name":{"type":"string","description":"Echo of the file name."}},"required":["bucket","path","upload_url","token","file_name"]},"AssessCertificatesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Certificate id."},"user_id":{"type":"string","format":"uuid","description":"Learner the certificate was issued to."},"course_id":{"type":"string","format":"uuid","description":"Course the certificate covers."},"template_id":{"type":["string","null"],"format":"uuid","description":"Certificate template used to render it."},"learner_name":{"type":"string","description":"The learner's name as printed on the certificate."},"issued_at":{"type":"string","format":"date-time","description":"When the certificate was issued."}},"required":["id","user_id","course_id","template_id","learner_name","issued_at"]},"AssessCertificatesIdPdfItem":{"type":"object","properties":{"certificate_id":{"type":"string","format":"uuid","description":"The certificate."},"format":{"type":"string","enum":["html"],"description":"Always `html` today: the printable page."},"pdf_available":{"type":"boolean","description":"False: no server-side PDF exists."},"print_url":{"type":"string","description":"The printable certificate page."},"verify_url":{"type":"string","description":"Public JSON verification."}},"required":["certificate_id","format","pdf_available","print_url","verify_url"]},"AssessCertificatesIssueItem":{"type":"object","properties":{"certificate_id":{"type":"string","format":"uuid","description":"Certificate id (use it with GET /assess/certificates/{id}/pdf)."},"created":{"type":"boolean","description":"False when the certificate already existed."},"learner_name":{"type":"string","description":"The name printed on it."},"issued_at":{"type":"string","format":"date-time","description":"The completion date it records."},"verify_url":{"type":"string","description":"Public JSON verification (no authentication)."},"print_url":{"type":"string","description":"The printable certificate page, with the organisation's branding."}},"required":["certificate_id","created","learner_name","issued_at","verify_url","print_url"]},"AssessCompletion-recordsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Completion record id."},"user_id":{"type":"string","format":"uuid","description":"Learner this record covers."},"course_id":{"type":["string","null"],"format":"uuid","description":"Live course id, if the course still exists."},"course_title":{"type":"string","description":"Course title as it was at the moment of completion."},"course_version":{"type":"integer","description":"The course.current_version the learner actually consumed."},"content_summary":{"type":"object","description":"Snapshot of module/lesson titles the learner completed, in order."},"attendee_name":{"type":"string","description":"The learner's name as of completion."},"attendee_job_title":{"type":["string","null"],"description":"The learner's job title as of completion."},"trainer_id":{"type":["string","null"],"format":"uuid","description":"The course owner/instructor at the time of completion, if known."},"trainer_name":{"type":["string","null"],"description":"Trainer name snapshot."},"trainer_qualifications":{"type":["string","null"],"description":"Trainer qualifications snapshot."},"score":{"type":["integer","null"],"description":"Final lesson score that completed the course, if scored."},"completed_at":{"type":"string","format":"date-time","description":"When the course was completed."}},"required":["id","user_id","course_id","course_title","course_version","content_summary","attendee_name","attendee_job_title","trainer_id","trainer_name","trainer_qualifications","score","completed_at"]},"AssessGrading-queueItem":{"type":"object","properties":{"attempt_id":{"type":"string","format":"uuid","description":"Attempt id."},"assessment_id":{"type":"string","format":"uuid","description":"Assessment id."},"assessment_title":{"type":["string","null"],"description":"Assessment title."},"user_id":{"type":"string","format":"uuid","description":"The learner."},"external_id":{"type":["string","null"],"description":"The learner's external id, when they were created through the API."},"attempt_no":{"type":"integer","description":"Attempt number."},"submitted_at":{"type":["string","null"],"format":"date-time","description":"When the learner submitted."},"responses":{"type":"array","description":"{ response_id, question_id, format, prompt, rubric_id, answer, is_correct, graded_score, needs_grading }."}},"required":["attempt_id","assessment_id","assessment_title","user_id","external_id","attempt_no","submitted_at","responses"]},"AssessQuestion-bankItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Question id."},"format":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Question format."},"prompt":{"type":"string","description":"The question text."},"tags":{"type":"array","description":"Free-form tags."},"difficulty":{"type":"string","enum":["easy","medium","hard"],"description":"Difficulty."},"points":{"type":"number","description":"Points the question is worth."},"config":{"type":"object","description":"Format-specific, answer-free configuration (the options of a multiple-choice question, a rubric id for an essay). Never holds the answer key."},"explanation":{"type":["string","null"],"description":"Shown to the learner in the post-attempt review."},"status":{"type":"string","enum":["draft","approved","archived"],"description":"Only approved questions can be drawn into an assessment. AI-generated questions start as draft."},"source":{"type":"string","description":"author or ai_generated."},"created_by":{"type":["string","null"],"format":"uuid","description":"Author."},"approved_by":{"type":["string","null"],"format":"uuid","description":"Who approved it."},"approved_at":{"type":["string","null"],"format":"date-time","description":"When it was approved."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","format","prompt","tags","difficulty","points","config","explanation","status","source","created_by","approved_by","approved_at","created_at","updated_at"]},"AssessQuestion-bankIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The deleted question id."},"deleted":{"type":"boolean","description":"true."}},"required":["id","deleted"]},"AssessQuestion-bankIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Question id."},"format":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Question format."},"prompt":{"type":"string","description":"The question text."},"tags":{"type":"array","description":"Free-form tags."},"difficulty":{"type":"string","enum":["easy","medium","hard"],"description":"Difficulty."},"points":{"type":"number","description":"Points the question is worth."},"config":{"type":"object","description":"Format-specific, answer-free configuration (the options of a multiple-choice question, a rubric id for an essay). Never holds the answer key."},"explanation":{"type":["string","null"],"description":"Shown to the learner in the post-attempt review."},"status":{"type":"string","enum":["draft","approved","archived"],"description":"Only approved questions can be drawn into an assessment. AI-generated questions start as draft."},"source":{"type":"string","description":"author or ai_generated."},"created_by":{"type":["string","null"],"format":"uuid","description":"Author."},"approved_by":{"type":["string","null"],"format":"uuid","description":"Who approved it."},"approved_at":{"type":["string","null"],"format":"date-time","description":"When it was approved."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","format","prompt","tags","difficulty","points","config","explanation","status","source","created_by","approved_by","approved_at","created_at","updated_at"]},"AssessQuestion-bankIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Question id."},"format":{"type":"string","enum":["multiple_choice","short_answer","matching","ordering","file_upload","essay"],"description":"Question format."},"prompt":{"type":"string","description":"The question text."},"tags":{"type":"array","description":"Free-form tags."},"difficulty":{"type":"string","enum":["easy","medium","hard"],"description":"Difficulty."},"points":{"type":"number","description":"Points the question is worth."},"config":{"type":"object","description":"Format-specific, answer-free configuration (the options of a multiple-choice question, a rubric id for an essay). Never holds the answer key."},"explanation":{"type":["string","null"],"description":"Shown to the learner in the post-attempt review."},"status":{"type":"string","enum":["draft","approved","archived"],"description":"Only approved questions can be drawn into an assessment. AI-generated questions start as draft."},"source":{"type":"string","description":"author or ai_generated."},"created_by":{"type":["string","null"],"format":"uuid","description":"Author."},"approved_by":{"type":["string","null"],"format":"uuid","description":"Who approved it."},"approved_at":{"type":["string","null"],"format":"date-time","description":"When it was approved."},"created_at":{"type":"string","format":"date-time","description":"Created."},"updated_at":{"type":"string","format":"date-time","description":"Last changed."}},"required":["id","format","prompt","tags","difficulty","points","config","explanation","status","source","created_by","approved_by","approved_at","created_at","updated_at"]},"AssessQuestion-bankIdAnswer-keyItem":{"type":"object","properties":{"question_id":{"type":"string","format":"uuid","description":"Question id."},"key":{"type":["object","null"],"description":"The key, in the shape of the question's format."}},"required":["question_id","key"]},"AssessQuestion-bankIdAnswer-keyPutItem":{"type":"object","properties":{"question_id":{"type":"string","format":"uuid","description":"Question id."},"updated":{"type":"boolean","description":"true."}},"required":["question_id","updated"]},"AssessQuestion-bankGenerateItem":{"type":"object","properties":{"questions":{"type":"array","description":"The saved drafts, shaped like GET /assess/question-bank items."},"model":{"type":"string","description":"The model that answered."},"provider":{"type":"string","description":"The provider that answered."}},"required":["questions","model","provider"]},"AssessReaction-formsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Form id."},"title":{"type":"string","description":"Form title."},"course_id":{"type":["string","null"],"format":"uuid","description":"The course it belongs to, if any."},"questions":{"type":"array","description":"{ id, label, type, options? } in display order. Answer keys by question id."},"anonymous":{"type":"boolean","description":"When true the response is stored without the learner's identity."},"is_active":{"type":"boolean","description":"Always true for a learner: inactive forms are invisible."}},"required":["id","title","course_id","questions","anonymous","is_active"]},"AssessReaction-formsIdSubmitItem":{"type":"object","properties":{"response_id":{"type":"string","format":"uuid","description":"The stored response."},"form_id":{"type":"string","format":"uuid","description":"The form."}},"required":["response_id","form_id"]},"AuthoringCoursesItem":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"The new draft course."},"job_id":{"type":"string","format":"uuid","description":"Poll GET /api/v1/authoring/jobs/{job_id}."}},"required":["course_id","job_id"]},"AuthoringCoursesIdExportItem":{"type":"object","properties":{"format":{"type":"string","description":"The format built."},"filename":{"type":"string","description":"Suggested file name."},"content_type":{"type":"string","description":"The file's media type."},"size_bytes":{"type":"integer","description":"Size of the file."},"download_url":{"type":"string","description":"Signed URL; valid until `expires_at`."},"expires_at":{"type":"string","format":"date-time","description":"When the URL stops working (one hour)."}},"required":["format","filename","content_type","size_bytes","download_url","expires_at"]},"AuthoringJobsIdItem":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid","description":"The job."},"course_id":{"type":["string","null"],"format":"uuid","description":"The course being built."},"status":{"type":"string","enum":["running","succeeded","failed","cancelled"],"description":"Overall state."},"stage":{"type":"string","enum":["blueprint","structure","lessons","apply","done"],"description":"Where the build is."},"progress":{"type":"integer","description":"0-100."},"error":{"type":["object","null"],"description":"`{ code, message }`, present when `status` is `failed`."},"result":{"type":["object","null"],"description":"On success: `{ drafts_applied, drafts_skipped, course_status }`."}},"required":["job_id","course_id","status","stage","progress","error","result"]},"AuthoringUploadsItem":{"type":"object","properties":{"upload_id":{"type":"string","format":"uuid","description":"Use as a source `value` with `type: \"upload\"`."},"upload_url":{"type":"string","description":"Signed URL to PUT the file to."},"method":{"type":"string","enum":["PUT"],"description":"HTTP method for `upload_url`."},"content_type":{"type":"string","description":"Send this as the Content-Type of the PUT."},"expires_at":{"type":"string","format":"date-time","description":"When the URL stops being valid (15 minutes)."}},"required":["upload_id","upload_url","method","content_type","expires_at"]},"CatalogueItemsIdAddItem":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"The new draft course."},"licence_source":{"type":"string","enum":["tenant","partner"],"description":"Whose licence the item was added under."}},"required":["course_id","licence_source"]},"CatalogueSearchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Item id; pass to POST /api/v1/catalogue/items/{id}/add."},"provider_key":{"type":"string","description":"The provider."},"provider_name":{"type":"string","description":"The provider's display name."},"licence_source":{"type":"string","enum":["tenant","partner"],"description":"Whose licence applies."},"title":{"type":"string","description":"Item title."},"description":{"type":["string","null"],"description":"Item description."},"url":{"type":"string","description":"Where the item is hosted."},"image_url":{"type":["string","null"],"description":"Thumbnail."},"duration_minutes":{"type":["integer","null"],"description":"Length, when the provider states it."},"language":{"type":["string","null"],"description":"Language code."},"licence_bucket":{"type":["string","null"],"description":"Licence bucket A-D."}},"required":["id","provider_key","provider_name","licence_source","title","description","url","image_url","duration_minutes","language","licence_bucket"]},"CommerceCartItem":{"type":"object","properties":{"cart":{"type":["object","null"],"description":"The open cart (`id`, `status`, `currency`), or null."},"items":{"type":"array","description":"Line items: `id`, `listing_id`, `quantity`, `unit_price_minor_units`, `currency`."}},"required":["cart","items"]},"CommerceCartPostItem":{"type":"object","properties":{"cart":{"type":["object","null"],"description":"The open cart after the change."},"items":{"type":"array","description":"Line items after the change."}},"required":["cart","items"]},"CommerceCheckoutItem":{"type":"object","properties":{"checkout_url":{"type":"string","description":"Open this URL in the learner's browser, top level, to pay."},"order":{"type":"object","description":"The pending order: `id`, `status`, `currency`, `total_minor_units`."}},"required":["checkout_url","order"]},"CommerceListingsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Listing id; send it to POST /api/v1/commerce/cart."},"course_id":{"type":["string","null"],"format":"uuid","description":"The course this listing sells access to."},"title":{"type":"string","description":"Listing title."},"description":{"type":"string","description":"Listing description."},"price_minor_units":{"type":"integer","description":"Price in minor units. 0 means free."},"currency":{"type":"string","description":"Lower-case ISO 4217 currency code."},"access_model":{"type":"string","enum":["perpetual","rental"],"description":"How long the purchase grants access: for good, or N days."},"access_days":{"type":["integer","null"],"description":"Days of access for a rental."},"created_at":{"type":"string","format":"date-time","description":"When the listing was created."}},"required":["id","course_id","title","description","price_minor_units","currency","access_model","access_days","created_at"]},"CommerceOrdersItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Order id."},"status":{"type":"string","enum":["pending","paid","failed","cancelled","refunded"],"description":"Order status."},"currency":{"type":"string","description":"Lower-case ISO 4217 currency code."},"subtotal_minor_units":{"type":"integer","description":"Subtotal in minor units."},"total_minor_units":{"type":"integer","description":"Total in minor units."},"provider":{"type":["string","null"],"description":"Payment provider that took the order."},"placed_at":{"type":"string","format":"date-time","description":"When the order was placed."},"paid_at":{"type":["string","null"],"format":"date-time","description":"When payment was confirmed."},"cancelled_at":{"type":["string","null"],"format":"date-time","description":"When the order was cancelled."}},"required":["id","status","currency","subtotal_minor_units","total_minor_units","provider","placed_at","paid_at","cancelled_at"]},"CommerceOrdersAllItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Order id."},"buyer_user_id":{"type":["string","null"],"format":"uuid","description":"The buying learner; null when the organisation itself is the buyer."},"status":{"type":"string","enum":["pending","paid","failed","cancelled","refunded"],"description":"Order status."},"currency":{"type":"string","description":"Lower-case ISO 4217 currency code."},"subtotal_minor_units":{"type":"integer","description":"Subtotal in minor units."},"total_minor_units":{"type":"integer","description":"Total in minor units."},"provider":{"type":["string","null"],"description":"Payment provider that took the order."},"subscription_id":{"type":["string","null"],"format":"uuid","description":"The subscription this order pays for, if any."},"placed_at":{"type":"string","format":"date-time","description":"When the order was placed."},"paid_at":{"type":["string","null"],"format":"date-time","description":"When payment was confirmed."},"cancelled_at":{"type":["string","null"],"format":"date-time","description":"When the order was cancelled."}},"required":["id","buyer_user_id","status","currency","subtotal_minor_units","total_minor_units","provider","subscription_id","placed_at","paid_at","cancelled_at"]},"DevelopmentEnrolment-requestsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Request id."},"user_id":{"type":"string","format":"uuid","description":"The requesting learner."},"external_id":{"type":["string","null"],"description":"The partner's external id for that learner."},"user_name":{"type":["string","null"],"description":"Display name, if one is set."},"course_id":{"type":"string","format":"uuid","description":"Course requested."},"course_title":{"type":"string","description":"Its title."},"status":{"type":"string","enum":["pending","approved","denied","withdrawn"],"description":"Request state."},"note":{"type":"string","description":"The learner's note."},"requested_at":{"type":"string","format":"date-time","description":"When it was made."},"decided_by":{"type":["string","null"],"format":"uuid","description":"Who decided."},"decided_at":{"type":["string","null"],"format":"date-time","description":"When."},"decision_note":{"type":["string","null"],"description":"The decision note."}},"required":["id","user_id","external_id","user_name","course_id","course_title","status","note","requested_at","decided_by","decided_at","decision_note"]},"DevelopmentEnrolment-requestsIdApproveItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Request id."},"status":{"type":"string","enum":["approved","denied"],"description":"The decision."},"enrolled":{"type":"boolean","description":"True when the learner is now enrolled in the course."}},"required":["id","status","enrolled"]},"DevelopmentEnrolment-requestsIdDenyItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Request id."},"status":{"type":"string","enum":["approved","denied"],"description":"The decision."},"enrolled":{"type":"boolean","description":"True when the learner is now enrolled in the course."}},"required":["id","status","enrolled"]},"DevelopmentLearning-requestsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Request id."},"title":{"type":"string","description":"What the learner needs training on (3 to 160 characters)."},"description":{"type":"string","description":"Detail."},"skill_id":{"type":["string","null"],"format":"uuid","description":"Skill it relates to."},"skill":{"type":["object","null"],"description":"{ id, name } of that skill."},"status":{"type":"string","enum":["open","planned","fulfilled","declined"],"description":"Administrator decision state."},"admin_note":{"type":["string","null"],"description":"Administrator's note."},"fulfilled_course_id":{"type":["string","null"],"format":"uuid","description":"Course that fulfilled it."},"votes":{"type":"integer","description":"Upvotes."},"created_at":{"type":"string","format":"date-time","description":"When it was submitted."},"updated_at":{"type":"string","format":"date-time","description":"Last change."},"my_vote":{"type":"boolean","description":"Whether the caller upvoted it."},"mine":{"type":"boolean","description":"Whether the caller wrote it."}},"required":["id","title","description","skill_id","skill","status","admin_note","fulfilled_course_id","votes","created_at","updated_at","my_vote","mine"]},"DevelopmentLearning-requestsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Request id."},"title":{"type":"string","description":"Title."},"description":{"type":"string","description":"Detail."},"skill_id":{"type":["string","null"],"format":"uuid","description":"Related skill."},"status":{"type":"string","description":"Decision state."},"votes":{"type":"integer","description":"Upvotes."},"mine":{"type":"boolean","description":"Always true here."}},"required":["id","title","description","skill_id","status","votes","mine"]},"DevelopmentLearning-requestsIdVoteItem":{"type":"object","properties":{"voted":{"type":"boolean","description":"True when the learner now has a vote on it."},"votes":{"type":["integer","null"],"description":"Total upvotes after the change."}},"required":["voted","votes"]},"DevelopmentObservationsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Observation id."},"learner_id":{"type":"string","format":"uuid","description":"The learner observed."},"learner_external_id":{"type":["string","null"],"description":"The partner's external id for that learner."},"manager_id":{"type":["string","null"],"format":"uuid","description":"The manager assigned to observe."},"competency_id":{"type":["string","null"],"format":"uuid","description":"Competency to verify, if any."},"status":{"type":"string","description":"\"requested\" or \"submitted\"."},"behavior_changed":{"type":["boolean","null"],"description":"Outcome: did behaviour change."},"rating":{"type":["integer","null"],"description":"Outcome: rating 1 to 5."},"notes":{"type":["string","null"],"description":"Request or outcome notes."},"observed_at":{"type":["string","null"],"format":"date-time","description":"When it was recorded."},"requested_at":{"type":"string","format":"date-time","description":"When it was requested."},"submitted_by":{"type":["string","null"],"format":"uuid","description":"Who requested or submitted."}},"required":["id","learner_id","learner_external_id","manager_id","competency_id","status","behavior_changed","rating","notes","observed_at","requested_at","submitted_by"]},"DevelopmentObservationsPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The new observation id."}},"required":["id"]},"DevelopmentObservationsIdRecordItem":{"type":"object","properties":{"ok":{"type":"boolean","description":"True when recorded."}},"required":["ok"]},"DevelopmentObservationsRequestItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The observation request id."}},"required":["id"]},"DevelopmentPlansItem":{"type":"object","properties":{"plan":{"type":["object","null"],"description":"{ id, title, description, updated_at }."},"steps":{"type":"array","description":"Ordered steps: { course_id, order_index, course: { id, title, description, duration_minutes } }."},"progress":{"type":["object","null"],"description":"{ total_steps, completed_steps, current_step_course_id, steps: [{ course_id, order_index, completed, unlocked }] }."}},"required":["plan","steps","progress"]},"DevelopmentPlansGenerateItem":{"type":"object","properties":{"pathId":{"type":"string","format":"uuid","description":"The personal plan's id."},"title":{"type":"string","description":"Plan title."},"description":{"type":"string","description":"Why these courses, in order."},"courseIds":{"type":"array","description":"Ordered course ids."},"disclosure":{"type":["string","null"],"description":"The AI-use disclosure a human must be shown for this feature, or null."}},"required":["pathId","title","description","courseIds","disclosure"]},"EmbedSessionsItem":{"type":"object","properties":{"url":{"type":"string","description":"The one-time URL to load in the iframe."},"expires_in":{"type":"integer","description":"Seconds until the URL stops working. Single use."}},"required":["url","expires_in"]},"EngageBadgesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Badge id."},"key":{"type":"string","description":"Stable badge key."},"name":{"type":"string","description":"Badge name."},"description":{"type":["string","null"],"description":"What it is for."},"icon":{"type":"string","description":"Icon name."},"points_value":{"type":"integer","description":"Points the badge is worth."},"awarded":{"type":"boolean","description":"Whether the learner earned it."},"awarded_at":{"type":["string","null"],"format":"date-time","description":"When they earned it."}},"required":["id","key","name","description","icon","points_value","awarded","awarded_at"]},"EngageDripCampaignsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Campaign id."},"title":{"type":"string","description":"Campaign title."},"cadence":{"type":"string","description":"\"daily\", \"every_other_day\", \"weekly\" or \"monthly\"."},"course_id":{"type":["string","null"],"format":"uuid","description":"Course the campaign reinforces."},"course_title":{"type":["string","null"],"description":"Title of that course."},"total_items":{"type":"integer","description":"Questions in the campaign."},"answered_items":{"type":"integer","description":"Questions this learner has answered."},"completed":{"type":"boolean","description":"True once every item is answered."}},"required":["id","title","cadence","course_id","course_title","total_items","answered_items","completed"]},"EngageDripCampaignsIdItemsItemIdCompleteItem":{"type":"object","properties":{"item_id":{"type":"string","format":"uuid","description":"The item that was completed."},"confident":{"type":"boolean","description":"The mark recorded."},"campaign":{"type":"object","description":"Campaign summary after the write, including `completed`."}},"required":["item_id","confident","campaign"]},"EngageDripCampaignsIdNextItem":{"type":"object","properties":{"campaign":{"type":"object","description":"Campaign summary: id, title, cadence, course_id, course_title, total_items, answered_items, completed."},"item":{"type":["object","null"],"description":"The next item: id, question, answer, order_index. Null when none remain."},"remaining":{"type":"integer","description":"Items still to answer, including this one."}},"required":["campaign","item","remaining"]},"EngageLeaderboardItem":{"type":"object","properties":{"rank":{"type":"integer","description":"Position, 1 is first."},"display_name":{"type":"string","description":"The learner's display name, or \"Learner\" when none is set."},"total_points":{"type":"integer","description":"Points earned in this organisation."},"is_me":{"type":"boolean","description":"True for the calling learner."}},"required":["rank","display_name","total_points","is_me"]},"EngageMicrolearningItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Campaign id."},"title":{"type":"string","description":"Campaign title."},"cadence":{"type":"string","description":"Delivery cadence."},"course_id":{"type":["string","null"],"format":"uuid","description":"Linked course."},"course_title":{"type":["string","null"],"description":"Linked course title."},"total_items":{"type":"integer","description":"Questions in the campaign."},"answered_items":{"type":"integer","description":"Questions answered by this learner."},"completed":{"type":"boolean","description":"Every item answered."},"items":{"type":"array","description":"Each item: id, question, answer, order_index, answered, confident."}},"required":["id","title","cadence","course_id","course_title","total_items","answered_items","completed","items"]},"EngageNotification-preferencesItem":{"type":"object","properties":{"kinds":{"type":"array","description":"One entry per kind: kind, label, description, category, channels, locked, enabled_channels."},"dm_channels_available":{"type":"object","description":"Whether the organisation has Slack / Teams direct messages configured: { slack_dm, teams_dm }."}},"required":["kinds","dm_channels_available"]},"EngageNotification-preferencesPutItem":{"type":"object","properties":{"saved":{"type":"array","description":"The updates that were stored."},"rejected":{"type":"array","description":"Updates that were refused, each with a reason."}},"required":["saved","rejected"]},"EngageNotificationsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Notification id."},"kind":{"type":"string","description":"Notification kind, e.g. `course.enrolled`."},"transactional":{"type":"boolean","description":"True for kinds the learner cannot silence."},"title":{"type":"string","description":"Headline."},"body":{"type":["string","null"],"description":"Optional detail."},"url":{"type":["string","null"],"description":"A path inside the platform this notification points at."},"data":{"type":"object","description":"Kind-specific identifiers (course_id, session_id, ...)."},"read_at":{"type":["string","null"],"format":"date-time","description":"When the learner read it."},"created_at":{"type":"string","format":"date-time","description":"When it was created."}},"required":["id","kind","transactional","title","body","url","data","read_at","created_at"]},"EngageNotificationsIdReadItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Notification id."},"read_at":{"type":"string","format":"date-time","description":"When it was marked read."}},"required":["id","read_at"]},"EngageNotificationsRead-allItem":{"type":"object","properties":{"marked_read":{"type":"integer","description":"How many notifications changed from unread to read."}},"required":["marked_read"]},"EngageNuggetsIdAnswerItem":{"type":"object","properties":{"accepted":{"type":"boolean","description":"True when the answer was recorded."},"interval_days":{"type":"integer","description":"Days until the card is due again."},"ease_factor":{"type":"number","description":"Legacy SM-2 ease factor, kept for compatibility. Scheduling now uses FSRS-5 (see stability and difficulty)."},"next_due_at":{"type":"string","format":"date-time","description":"When the card is next due."},"stability":{"type":"number","description":"FSRS-5 memory stability in days after this answer."},"difficulty":{"type":"number","description":"FSRS-5 difficulty after this answer, from 1 (easiest) to 10 (hardest)."},"retrievability":{"type":["number","null"],"description":"Probability of recall at the moment of answering; null for a new card."},"rating":{"type":"integer","description":"The FSRS rating applied: 1 again, 2 hard, 3 good, 4 easy."}},"required":["accepted","interval_days","ease_factor","next_due_at","stability","difficulty","retrievability","rating"]},"EngageNuggetsDailyItem":{"type":"object","properties":{"id":{"type":"string","description":"Opaque nugget id."},"lesson_id":{"type":["string","null"],"format":"uuid","description":"Lesson the card belongs to. Null for a card the learner made themselves."},"card_kind":{"type":"string","description":"`lesson` for a card from a lesson, `own` for a card the learner made from a Learn Anything explainer."},"lesson_title":{"type":"string","description":"Title of that lesson, or the topic of an own card."},"front":{"type":"string","description":"The question side."},"back":{"type":"string","description":"The answer side."},"hint":{"type":["string","null"],"description":"Optional hint."},"next_due_at":{"type":["string","null"],"format":"date-time","description":"When the card was due."},"is_new":{"type":"boolean","description":"True when the learner has never reviewed this card."}},"required":["id","lesson_id","card_kind","lesson_title","front","back","hint","next_due_at","is_new"]},"EngagePointsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Ledger entry id."},"event_type":{"type":"string","description":"\"lesson_completed\", \"course_completed\" or \"badge_earned\"."},"points":{"type":"integer","description":"Points awarded."},"ref_id":{"type":"string","format":"uuid","description":"The lesson, course or badge the points were for."},"created_at":{"type":"string","format":"date-time","description":"When they were awarded."}},"required":["id","event_type","points","ref_id","created_at"]},"EngageRecommendationsItem":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"Recommended course."},"score":{"type":"number","description":"Relative rank score."},"reasons":{"type":"array","description":"Up to three reasons, e.g. \"Because you completed GDPR Basics\"."},"course":{"type":"object","description":"id, title, description, difficulty_level, duration_minutes, thumbnail_url, delivery_type, is_required."}},"required":["course_id","score","reasons","course"]},"EngageStreakItem":{"type":"object","properties":{"current_streak":{"type":"integer","description":"Consecutive days of activity ending today or yesterday."},"longest_streak":{"type":"integer","description":"Best streak so far."},"last_activity_date":{"type":["string","null"],"format":"date","description":"Last day with activity."}},"required":["current_streak","longest_streak","last_activity_date"]},"EventsRegistrationsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Registration id."},"session_id":{"type":"string","format":"uuid","description":"Session registered for."},"user_id":{"type":"string","format":"uuid","description":"Registered learner."},"status":{"type":"string","description":"\"confirmed\", \"waitlisted\" or \"cancelled\"."},"waitlist_position":{"type":["integer","null"],"description":"Position in the waitlist, if waitlisted."},"registered_at":{"type":"string","format":"date-time","description":"When the registration was made."},"cancelled_at":{"type":["string","null"],"format":"date-time","description":"When the registration was cancelled, if it was."}},"required":["id","session_id","user_id","status","waitlist_position","registered_at","cancelled_at"]},"EventsRegistrationsPostItem":{"type":"object","properties":{"status":{"type":"string","enum":["confirmed","waitlisted"],"description":"Outcome of the registration."},"waitlist_position":{"type":["integer","null"],"description":"Position in the waitlist, if waitlisted."},"already":{"type":"boolean","description":"True if the learner was already registered (no notification was sent, no new row)."}},"required":["status","waitlist_position","already"]},"EventsRegistrationsIdCancelItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Registration id."},"session_id":{"type":"string","format":"uuid","description":"The session it was for."},"status":{"type":"string","description":"Always \"cancelled\"."},"already":{"type":"boolean","description":"True when it had already been cancelled."},"promoted":{"type":"boolean","description":"True when a waitlisted learner took the freed seat."}},"required":["id","session_id","status","already","promoted"]},"EventsSessionsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Session id."},"course_id":{"type":["string","null"],"format":"uuid","description":"Course this session belongs to, if any."},"title":{"type":"string","description":"Session title."},"description":{"type":"string","description":"Session description."},"delivery_mode":{"type":"string","description":"\"in_person\" or \"virtual\" (see the session's own schema for the exact enum)."},"location":{"type":["string","null"],"description":"Physical location, for in-person sessions."},"virtual_provider":{"type":["string","null"],"description":"Virtual meeting provider, for virtual sessions."},"trainer_id":{"type":["string","null"],"format":"uuid","description":"Assigned trainer."},"starts_at":{"type":"string","format":"date-time","description":"Start time."},"ends_at":{"type":"string","format":"date-time","description":"End time."},"session_timezone":{"type":"string","description":"IANA timezone the times above are anchored to."},"capacity":{"type":"integer","description":"Maximum confirmed registrations."},"status":{"type":"string","description":"e.g. \"scheduled\", \"cancelled\"."},"created_at":{"type":"string","format":"date-time","description":"When the session was created."}},"required":["id","course_id","title","description","delivery_mode","location","virtual_provider","trainer_id","starts_at","ends_at","session_timezone","capacity","status","created_at"]},"EventsSessionsIdAttendanceItem":{"type":"object","properties":{"ok":{"type":"boolean","description":"True when recorded."},"user_id":{"type":"string","format":"uuid","description":"The learner marked."},"attended":{"type":"boolean","description":"The mark recorded."}},"required":["ok","user_id","attended"]},"EventsSessionsIdAttendanceClaimItem":{"type":"object","properties":{"result":{"type":"object","description":"The outcome of claim_session_completion(), e.g. { ok, reason?, ... }."}},"required":["result"]},"GraphqlItem":{"type":"object","properties":{"data":{"type":["object","null"],"description":"The selected fields. A field the key may not read is null here with a matching entry in `errors`."},"errors":{"type":["array","null"],"description":"GraphQL errors. `extensions.code` carries the same machine-readable code the REST API would have returned."}},"required":["data","errors"]},"HooksItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Hook id — pass to DELETE /api/v1/hooks/{id} to unsubscribe."},"event":{"type":"string","description":"The event this hook is subscribed to."},"target_url":{"type":"string","description":"Where deliveries are sent."},"description":{"type":"string","description":"Human-readable label, e.g. \"Zapier: course.published\"."},"is_active":{"type":"boolean","description":"Whether deliveries are currently being attempted."},"source":{"type":"string","enum":["api","zapier","make","n8n"],"description":"What created this hook."},"secret_hint":{"type":"string","description":"Non-secret suffix of the signing secret, e.g. \"whsec_…ab12\". The full secret is never returned again after creation."},"created_at":{"type":"string","format":"date-time","description":"When the hook was created."},"last_delivery_at":{"type":["string","null"],"format":"date-time","description":"Most recent delivery attempt, if any."},"last_delivery_status":{"type":["string","null"],"description":"Outcome of the most recent delivery attempt, if any."}},"required":["id","event","target_url","description","is_active","source","secret_hint","created_at","last_delivery_at","last_delivery_status"]},"HooksPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Hook id."},"event":{"type":"string","description":"The subscribed event."},"target_url":{"type":"string","description":"The URL deliveries will be sent to."},"secret_hint":{"type":"string","description":"Non-secret suffix of the signing secret. The full secret is generated server-side and is deliberately NOT returned here — see docs/api-v1.md's REST Hooks section for why, and how to verify a delivery's signature."},"signing":{"type":"string","description":"Always \"see docs\" — points at the signature verification section."}},"required":["id","event","target_url","secret_hint","signing"]},"HooksIdItem":{"type":"object","properties":{},"required":[]},"HooksSamplesItem":{"type":"object","properties":{"event":{"type":"string","description":"The event these samples are for."},"synthetic":{"type":"boolean","description":"True when there is no real delivery history for this event yet, so the sample below is illustrative, not a real payload."},"samples":{"type":"array","description":"One to three payload objects, newest first."}},"required":["event","synthetic","samples"]},"InsightsAt-riskItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"Learner id."},"full_name":{"type":["string","null"],"description":"Learner display name."},"course_id":{"type":"string","format":"uuid","description":"Course id."},"course_title":{"type":["string","null"],"description":"Course title."},"enrolled_at":{"type":"string","format":"date-time","description":"When the learner enrolled."},"due_at":{"type":"string","format":"date-time","description":"Assignment due date."},"last_activity_at":{"type":["string","null"],"format":"date-time","description":"Most recent lesson_progress completion for this course."},"days_since_activity":{"type":["integer","null"],"description":"Days since last_activity_at, or null if there has been none."},"pct_time_elapsed":{"type":["number","null"],"description":"Percentage of the enrolled-to-due window already elapsed."},"reason_no_recent_activity":{"type":"boolean","description":"No activity in the last 14 days."},"reason_past_halfway":{"type":"boolean","description":"Past the halfway point between enrolment and due date."},"reason_due_soon":{"type":"boolean","description":"Due within the next 7 days."}},"required":["user_id","full_name","course_id","course_title","enrolled_at","due_at","last_activity_at","days_since_activity","pct_time_elapsed","reason_no_recent_activity","reason_past_halfway","reason_due_soon"]},"InsightsCompletionsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Completion record id."},"user_id":{"type":"string","format":"uuid","description":"Learner who completed the course."},"course_id":{"type":["string","null"],"format":"uuid","description":"Course completed (null if the course was later deleted — see course_title for the snapshot)."},"course_title":{"type":"string","description":"Course title at the moment of completion."},"score":{"type":["integer","null"],"description":"Score achieved, if scored."},"completed_at":{"type":"string","format":"date-time","description":"When the course was completed."},"expires_at":{"type":["string","null"],"format":"date-time","description":"When this completion expires, if it is subject to recertification."},"recert_status":{"type":"string","description":"Recertification status, e.g. \"not_applicable\"."}},"required":["id","user_id","course_id","course_title","score","completed_at","expires_at","recert_status"]},"InsightsKirkpatrickItem":{"type":"object","properties":{"level1":{"type":"array","description":"Reaction. Rows of { level, metric, value, unit, sample_size, source, note }."},"level2":{"type":"array","description":"Learning. Rows of { level, metric, value, unit, sample_size, source, note }."},"level3":{"type":"array","description":"Behaviour. Rows of { level, metric, value, unit, sample_size, source, note }."},"level4":{"type":"array","description":"Results (customer supplied). Rows of { level, metric, value, unit, sample_size, source, note }."}},"required":["level1","level2","level3","level4"]},"InsightsReportingDatasetItem":{"type":"object","properties":{"day":{"type":["string","null"],"format":"date","description":"UTC day (week_start for enrolments). Every dataset documents its own columns in integrations/bi-templates/README.md."},"value":{"type":["number","null"],"description":"The value; null when suppressed."},"n":{"type":["integer","null"],"description":"People behind the value; null when suppressed."},"suppressed":{"type":"boolean","description":"True when a value in the row is hidden because its group is below the minimum."},"min_group_size":{"type":"integer","description":"The group minimum applied."}},"required":["day","value","n","suppressed","min_group_size"]},"InsightsRoiItem":{"type":"object","properties":{"measured_facts":{"type":"array","description":"{ metric, value, source: \"measured\" }."},"assumptions_used":{"type":"array","description":"The editable assumptions the figures rest on."},"line_items":{"type":"array","description":"{ label, category, source, formula, amount }."},"total_cost":{"type":"number","description":"Sum of cost line items."},"total_benefit":{"type":"number","description":"Sum of benefit line items."},"net_benefit":{"type":"number","description":"Benefit minus cost."},"roi_percent":{"type":["number","null"],"description":"Null when there is no cost to divide by."},"formula":{"type":"string","description":"How the figure is derived."},"caveat":{"type":"string","description":"What the figure does and does not mean."}},"required":["measured_facts","assumptions_used","line_items","total_cost","total_benefit","net_benefit","roi_percent","formula","caveat"]},"KnowledgeAnswerItem":{"type":"object","properties":{"refused":{"type":"boolean","description":"True when no answer could be given from the learner's sources."},"reason":{"type":["string","null"],"description":"When refused: `no_sources` or `insufficient`."},"answer":{"type":["string","null"],"description":"The answer, with [n] markers that refer to `citations`."},"citations":{"type":["array","null"],"description":"`[{ n, title, href, source_type }]`; `href` is null when the source has no page the learner can open."},"disclosure":{"type":["string","null"],"description":"The AI-interaction notice your UI must show (EU AI Act Article 50), or null."}},"required":["refused","reason","answer","citations","disclosure"]},"KnowledgeArticlesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Article id."},"title":{"type":"string","description":"Title."},"slug":{"type":"string","description":"URL slug, unique within the organisation. Accepted wherever an article id is."},"summary":{"type":["string","null"],"description":"Short summary."},"category_id":{"type":["string","null"],"format":"uuid","description":"Category id."},"category":{"type":["object","null"],"description":"The category: `{ id, name, icon, color }`."},"author_id":{"type":"string","format":"uuid","description":"The author's user id."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Publication state. Readers see published articles only; authors also see their own drafts."},"is_featured":{"type":"boolean","description":"Featured on the knowledge home."},"view_count":{"type":"integer","description":"Stored view count."},"tags":{"type":"array","description":"Tag names."},"published_at":{"type":["string","null"],"format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","title","slug","summary","category_id","category","author_id","status","is_featured","view_count","tags","published_at","created_at","updated_at"]},"KnowledgeArticlesPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Article id."},"title":{"type":"string","description":"Title."},"slug":{"type":"string","description":"URL slug, unique within the organisation. Accepted wherever an article id is."},"summary":{"type":["string","null"],"description":"Short summary."},"category_id":{"type":["string","null"],"format":"uuid","description":"Category id."},"category":{"type":["object","null"],"description":"The category: `{ id, name, icon, color }`."},"author_id":{"type":"string","format":"uuid","description":"The author's user id."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Publication state. Readers see published articles only; authors also see their own drafts."},"is_featured":{"type":"boolean","description":"Featured on the knowledge home."},"view_count":{"type":"integer","description":"Stored view count."},"tags":{"type":"array","description":"Tag names."},"published_at":{"type":["string","null"],"format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."},"body":{"type":"string","description":"The article text (Markdown)."},"helpful_count":{"type":"integer","description":"Learners who marked it helpful."},"not_helpful_count":{"type":"integer","description":"Learners who marked it not helpful."},"viewer_reaction":{"type":["string","null"],"enum":["helpful","not_helpful"],"description":"The learner's own reaction, if any."},"comment_count":{"type":"integer","description":"Comments on the article."}},"required":["id","title","slug","summary","category_id","category","author_id","status","is_featured","view_count","tags","published_at","created_at","updated_at","body","helpful_count","not_helpful_count","viewer_reaction","comment_count"]},"KnowledgeArticlesIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The deleted article id."}},"required":["id"]},"KnowledgeArticlesIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Article id."},"title":{"type":"string","description":"Title."},"slug":{"type":"string","description":"URL slug, unique within the organisation. Accepted wherever an article id is."},"summary":{"type":["string","null"],"description":"Short summary."},"category_id":{"type":["string","null"],"format":"uuid","description":"Category id."},"category":{"type":["object","null"],"description":"The category: `{ id, name, icon, color }`."},"author_id":{"type":"string","format":"uuid","description":"The author's user id."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Publication state. Readers see published articles only; authors also see their own drafts."},"is_featured":{"type":"boolean","description":"Featured on the knowledge home."},"view_count":{"type":"integer","description":"Stored view count."},"tags":{"type":"array","description":"Tag names."},"published_at":{"type":["string","null"],"format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."},"body":{"type":"string","description":"The article text (Markdown)."},"helpful_count":{"type":"integer","description":"Learners who marked it helpful."},"not_helpful_count":{"type":"integer","description":"Learners who marked it not helpful."},"viewer_reaction":{"type":["string","null"],"enum":["helpful","not_helpful"],"description":"The learner's own reaction, if any."},"comment_count":{"type":"integer","description":"Comments on the article."}},"required":["id","title","slug","summary","category_id","category","author_id","status","is_featured","view_count","tags","published_at","created_at","updated_at","body","helpful_count","not_helpful_count","viewer_reaction","comment_count"]},"KnowledgeArticlesIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Article id."},"title":{"type":"string","description":"Title."},"slug":{"type":"string","description":"URL slug, unique within the organisation. Accepted wherever an article id is."},"summary":{"type":["string","null"],"description":"Short summary."},"category_id":{"type":["string","null"],"format":"uuid","description":"Category id."},"category":{"type":["object","null"],"description":"The category: `{ id, name, icon, color }`."},"author_id":{"type":"string","format":"uuid","description":"The author's user id."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Publication state. Readers see published articles only; authors also see their own drafts."},"is_featured":{"type":"boolean","description":"Featured on the knowledge home."},"view_count":{"type":"integer","description":"Stored view count."},"tags":{"type":"array","description":"Tag names."},"published_at":{"type":["string","null"],"format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."},"body":{"type":"string","description":"The article text (Markdown)."},"helpful_count":{"type":"integer","description":"Learners who marked it helpful."},"not_helpful_count":{"type":"integer","description":"Learners who marked it not helpful."},"viewer_reaction":{"type":["string","null"],"enum":["helpful","not_helpful"],"description":"The learner's own reaction, if any."},"comment_count":{"type":"integer","description":"Comments on the article."}},"required":["id","title","slug","summary","category_id","category","author_id","status","is_featured","view_count","tags","published_at","created_at","updated_at","body","helpful_count","not_helpful_count","viewer_reaction","comment_count"]},"KnowledgeArticlesIdCommentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Comment id."},"article_id":{"type":"string","format":"uuid","description":"The article."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The comment replied to, or null."},"body":{"type":"string","description":"Comment text."},"author":{"type":"object","description":"`{ id, name, avatar_url }`."},"created_at":{"type":"string","format":"date-time","description":"When it was written."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","article_id","parent_id","body","author","created_at","updated_at"]},"KnowledgeArticlesIdCommentsCommentIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The deleted comment id."}},"required":["id"]},"KnowledgeArticlesIdCommentsCommentIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Comment id."},"article_id":{"type":"string","format":"uuid","description":"The article."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The comment replied to, or null."},"body":{"type":"string","description":"Comment text."},"author":{"type":"object","description":"`{ id, name, avatar_url }`."},"created_at":{"type":"string","format":"date-time","description":"When it was written."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","article_id","parent_id","body","author","created_at","updated_at"]},"KnowledgeArticlesIdReactionsItem":{"type":"object","properties":{"reaction":{"type":["string","null"],"enum":["helpful","not_helpful"],"description":"The reaction the learner now holds, or null."},"counts":{"type":"object","description":"Reaction counts now: `{ helpful, not_helpful }`."}},"required":["reaction","counts"]},"KnowledgeCategoriesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Category id."},"name":{"type":"string","description":"Category name."},"description":{"type":["string","null"],"description":"What it covers."},"icon":{"type":"string","description":"Icon name."},"color":{"type":"string","description":"Display colour."},"parent_id":{"type":["string","null"],"format":"uuid","description":"Parent category, if nested."},"order_index":{"type":"integer","description":"Display order."}},"required":["id","name","description","icon","color","parent_id","order_index"]},"KnowledgeSearchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Article id."},"title":{"type":"string","description":"Title."},"slug":{"type":"string","description":"URL slug, unique within the organisation. Accepted wherever an article id is."},"summary":{"type":["string","null"],"description":"Short summary."},"category_id":{"type":["string","null"],"format":"uuid","description":"Category id."},"category":{"type":["object","null"],"description":"The category: `{ id, name, icon, color }`."},"author_id":{"type":"string","format":"uuid","description":"The author's user id."},"status":{"type":"string","enum":["draft","published","archived"],"description":"Publication state. Readers see published articles only; authors also see their own drafts."},"is_featured":{"type":"boolean","description":"Featured on the knowledge home."},"view_count":{"type":"integer","description":"Stored view count."},"tags":{"type":"array","description":"Tag names."},"published_at":{"type":["string","null"],"format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","title","slug","summary","category_id","category","author_id","status","is_featured","view_count","tags","published_at","created_at","updated_at"]},"LearnAnythingItem":{"type":"object","properties":{"status":{"type":"string","description":"`ok` or `not_found`."},"session_id":{"type":["string","null"],"format":"uuid","description":"The saved session (status `ok`)."},"result":{"type":["object","null"],"description":"status `ok`: `{ mode, exercise (title, topic, summary, mental_model, real_world_case, reflection_question), citations: [{ n, title, href, source_type }], label, disclosure }`. The reflection model answer is not included."},"reason":{"type":["string","null"],"description":"status `not_found`: `no_sources`, `insufficient` or `knowledge_unavailable`."},"general_allowed":{"type":["boolean","null"],"description":"status `not_found`: whether `mode: \"general\"` is allowed for this organisation."}},"required":["status","session_id","result","reason","general_allowed"]},"LearnCoursesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Course id."},"title":{"type":"string","description":"Course title."},"description":{"type":["string","null"],"description":"Course description."},"status":{"type":"string","description":"Publication status."},"difficulty_level":{"type":["string","null"],"description":"Author-set difficulty label."},"duration_minutes":{"type":["integer","null"],"description":"Planned completion time set by the author."},"estimated_minutes":{"type":["integer","null"],"description":"Estimated learner minutes from the lesson content (falls back to duration_minutes, the plan)."},"created_at":{"type":"string","format":"date-time","description":"When the course was created."},"updated_at":{"type":"string","format":"date-time","description":"When the course was last updated."}},"required":["id","title","description","status","difficulty_level","duration_minutes","estimated_minutes","created_at","updated_at"]},"LearnCoursesIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Course id."},"title":{"type":"string","description":"Course title."},"description":{"type":["string","null"],"description":"Course description."},"status":{"type":"string","description":"Publication status."},"difficulty_level":{"type":["string","null"],"description":"Author-set difficulty label."},"duration_minutes":{"type":["integer","null"],"description":"Planned completion time set by the author."},"estimated_minutes":{"type":["integer","null"],"description":"Estimated learner minutes from the lesson content (falls back to duration_minutes, the plan)."},"created_at":{"type":"string","format":"date-time","description":"When the course was created."},"updated_at":{"type":"string","format":"date-time","description":"When the course was last updated."}},"required":["id","title","description","status","difficulty_level","duration_minutes","estimated_minutes","created_at","updated_at"]},"LearnCoursesIdLessonsItem":{"type":"object","properties":{"course":{"type":"object","description":"id, title, status, delivery_type."},"modules":{"type":"array","description":"Modules in order, each with id, title, order_index and lessons[] (id, title, duration_minutes, estimated_minutes, order_index)."}},"required":["course","modules"]},"LearnCoursesIdOutlineItem":{"type":"object","properties":{"course":{"type":"object","description":"id, title, description, status, delivery_type, prerequisites."},"enrolment":{"type":["object","null"],"description":"The learner's enrolment: id, enrolled_at, due_at, completed_at, last_lesson_id. Null when not enrolled."},"locked":{"type":"boolean","description":"True when the learner cannot open the course right now."},"lock_reason":{"type":["string","null"],"description":"`access_expired` or `views_spent`, or null."},"progress":{"type":"object","description":"completed_lessons, total_lessons, percent."},"modules":{"type":"array","description":"Modules in order, each with id, title, order_index and lessons[] (id, title, duration_minutes, estimated_minutes, order_index, completed, position_seconds, locked, lock_reason)."}},"required":["course","enrolment","locked","lock_reason","progress","modules"]},"LearnEnrolmentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Enrolment id."},"user_id":{"type":"string","format":"uuid","description":"Enrolled learner."},"course_id":{"type":"string","format":"uuid","description":"Enrolled course."},"enrolled_at":{"type":"string","format":"date-time","description":"When the enrolment was created."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the course was completed, or null if not yet completed."},"last_lesson_id":{"type":["string","null"],"format":"uuid","description":"The learner's most recently visited lesson in this course."}},"required":["id","user_id","course_id","enrolled_at","completed_at","last_lesson_id"]},"LearnEnrolmentsPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Enrolment id."},"user_id":{"type":"string","format":"uuid","description":"Enrolled learner."},"course_id":{"type":"string","format":"uuid","description":"Enrolled course."},"enrolled_at":{"type":"string","format":"date-time","description":"When the enrolment was created."},"due_at":{"type":["string","null"],"format":"date-time","description":"Due date, if one was given."}},"required":["id","user_id","course_id","enrolled_at","due_at"]},"LearnEnrolmentsSelfItem":{"type":"object","properties":{"mode":{"type":"string","enum":["enrolled","pending"],"description":"enrolled, or pending approval."},"request_id":{"type":["string","null"],"format":"uuid","description":"pending only: the enrolment request."},"enrolment":{"type":["object","null"],"description":"enrolled only: id, course_id, enrolled_at, due_at, completed_at."}},"required":["mode","request_id","enrolment"]},"LearnLessonsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Lesson id."},"course_id":{"type":"string","format":"uuid","description":"Course the lesson belongs to."},"module_id":{"type":"string","format":"uuid","description":"Module the lesson belongs to."},"title":{"type":"string","description":"Lesson title."},"duration_minutes":{"type":["integer","null"],"description":"Planned minutes set by the author."},"estimated_minutes":{"type":["integer","null"],"description":"Estimated learner minutes from the lesson content (falls back to duration_minutes)."},"order_index":{"type":"integer","description":"Position in the module."},"hero_image_url":{"type":["string","null"],"description":"Hero image (may be a `storage:` reference; see media_urls)."},"content":{"type":["object","null"],"description":"The content document: `{ blocks: [...] }`. Scored quiz blocks have no answer key."},"content_locale":{"type":"string","description":"Locale of `content` (`en` for the original)."},"media_urls":{"type":"object","description":"Map of `storage:` reference to signed URL."},"media_urls_expire_in":{"type":["integer","null"],"description":"Seconds the signed URLs stay valid."},"progress":{"type":"object","description":"completed and position_seconds for this learner."},"my_quiz_responses":{"type":"array","description":"The learner's own answers so far: block_id, question_index, selected_answer, is_correct."}},"required":["id","course_id","module_id","title","duration_minutes","estimated_minutes","order_index","hero_image_url","content","content_locale","media_urls","media_urls_expire_in","progress","my_quiz_responses"]},"LearnLessonsIdProgressItem":{"type":"object","properties":{"completed":{"type":"boolean","description":"Whether the lesson is now complete for this learner."},"course_completed":{"type":"boolean","description":"Whether this made, or found, the whole course complete."},"reason":{"type":["string","null"],"description":"complete only: why completion was refused."},"certificate_code":{"type":["string","null"],"description":"complete only: set when the course completion issued a certificate."}},"required":["completed","course_completed","reason","certificate_code"]},"LearnLessonsIdQuiz-answersItem":{"type":"object","properties":{"accepted":{"type":"boolean","description":"Whether the answer was recorded."},"reason":{"type":["string","null"],"description":"Why it was not recorded."},"is_correct":{"type":["boolean","null"],"description":"Correctness of the recorded answer."},"correct_answer":{"type":["integer","null"],"description":"The right option; only for a right answer, the last attempt or a final pass."},"attempt_no":{"type":["integer","null"],"description":"The pass this answer counts toward."},"attempts_used":{"type":["integer","null"],"description":"Passes resolved so far."},"max_attempts":{"type":["integer","null"],"description":"Pass limit (0 = unlimited)."},"attempts_remaining":{"type":["integer","null"],"description":"Passes left, or null when unlimited."},"question_index":{"type":"integer","description":"Echo of the question index."}},"required":["accepted","reason","is_correct","correct_answer","attempt_no","attempts_used","max_attempts","attempts_remaining","question_index"]},"LearnScormPackageIdAttemptItem":{"type":"object","properties":{"package_id":{"type":"string","format":"uuid","description":"Package id."},"schema_version":{"type":"string","description":"The package standard: `1.2`, `2004` or cmi5."},"launch_href":{"type":"string","description":"The package's launch file, relative to the package root."},"attempt":{"type":["object","null"],"description":"The stored attempt row (id, lesson_status, score_raw, score_min, score_max, suspend_data, session_time, lesson_location, exit_mode, entry, exit_at, timestamps), or null."},"scos":{"type":"array","description":"2004 only: id, title, scorm_type and status per SCO."},"current":{"type":["string","null"],"description":"2004 only: the SCO to resume on."},"sequencing_ignored":{"type":"boolean","description":"2004 only: whether the package used sequencing, which this platform ignores."}},"required":["package_id","schema_version","launch_href","attempt","scos","current","sequencing_ignored"]},"LearnScormPackageIdAttemptPostItem":{"type":"object","properties":{"attemptId":{"type":"string","format":"uuid","description":"The attempt to send to the track endpoint."},"schemaVersion":{"type":"string","description":"The package standard."},"launchUrl":{"type":"string","description":"Session-cookie URL of the launch file; not usable with an API key."}},"required":["attemptId","schemaVersion","launchUrl"]},"LearnScormPackageIdTrackItem":{"type":"object","properties":{"ok":{"type":"boolean","description":"True when the call was applied."},"completion":{"type":["object","null"],"description":"complete_lesson() result when the status is completed or passed: completed, course_completed, reason, ..."},"lessonStatus":{"type":["string","null"],"description":"2004 only: the rolled-up lesson status."},"scos":{"type":"object","description":"2004 only: display status per SCO id."},"current":{"type":["string","null"],"description":"2004 only: the current SCO id."}},"required":["ok","completion","lessonStatus","scos","current"]},"LimitsItem":{"type":"object","properties":{"published":{"type":"boolean","description":"False when the rate-limit policy could not be read; every figure below is then absent rather than guessed."},"policy_name":{"type":["string","null"],"description":"Identifier of the rate-limit policy this API is under."},"window_seconds":{"type":["integer","null"],"description":"Length of the fixed counting window, in seconds."},"default_limit":{"type":["integer","null"],"description":"Requests per window included with every plan, before any purchased increase."},"entitlement_lookup_key":{"type":["string","null"],"description":"The entitlement that raises the ceiling when purchased."},"setting_key":{"type":["string","null"],"description":"The organisation setting that can LOWER its own ceiling. It can never raise it above what the plan includes."},"scope_description":{"type":"string","description":"What the counter counts, and what it does not."},"headers":{"type":"array","description":"The response headers this API sends so a client can pace itself without being refused first."},"your_limit":{"type":["integer","null"],"description":"Your organisation's effective requests-per-window ceiling. Present only when a valid key is supplied."},"your_limit_source":{"type":["string","null"],"description":"How your ceiling was arrived at. Present only when a valid key is supplied."},"query_bounds":{"type":"object","description":"Bounds on the shape of a single request: REST page sizes and the GraphQL cost ceiling."},"policies":{"type":"array","description":"Every rate-limit policy: policy_name, key_kind (customer, partner, or test for sandbox keys), status (set, or pending when the number is what is enforced today and the published limit is not decided yet), window_seconds, default_limit, note."},"your_policy":{"type":["string","null"],"description":"The policy your key is counted under. Present only when a valid key is supplied."},"webhook_delivery":{"type":"object","description":"Webhook retry schedule (attempts, backoff, dead-letter) and the volume limit, with its status."}},"required":["published","policy_name","window_seconds","default_limit","entitlement_lookup_key","setting_key","scope_description","headers","your_limit","your_limit_source","query_bounds","policies","your_policy","webhook_delivery"]},"MeProfileItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"The learner's user id."},"external_id":{"type":["string","null"],"description":"The partner's external id for this learner, if any."},"display_name":{"type":"string","description":"Name shown on certificates and in lists. May be empty for a pseudonymous learner."},"avatar_url":{"type":["string","null"],"description":"https URL of the avatar."},"title":{"type":"string","description":"Job title."},"bio":{"type":["string","null"],"description":"Short bio."},"expertise":{"type":"array","description":"Interests / expertise, strings."},"locale":{"type":["string","null"],"description":"Language code, e.g. \"en\"."},"timezone":{"type":["string","null"],"description":"IANA timezone."},"leaderboard_opt_in":{"type":"boolean","description":"Whether the learner appears on leaderboards. Off by default."},"department":{"type":["string","null"],"description":"Department name (read-only; an administrator sets it)."}},"required":["user_id","external_id","display_name","avatar_url","title","bio","expertise","locale","timezone","leaderboard_opt_in","department"]},"MeRecordItem":{"type":"object","properties":{"id":{"type":"string","description":"Row id."},"type":{"type":"string","description":"Row type."},"typeLabel":{"type":"string","description":"Display label of the type."},"title":{"type":"string","description":"Title."},"status":{"type":"string","description":"Status text."},"date":{"type":["string","null"],"format":"date-time","description":"Relevant date."},"year":{"type":["integer","null"],"description":"Year of that date."},"hours":{"type":["number","null"],"description":"Hours spent."},"detail":{"type":"string","description":"Extra detail."},"verifyUrl":{"type":["string","null"],"description":"Public verification link, for certificates."},"verifyCode":{"type":["string","null"],"description":"Verification code."},"expiresAt":{"type":["string","null"],"format":"date-time","description":"Recertification expiry."},"courseId":{"type":["string","null"],"format":"uuid","description":"Course the row relates to."}},"required":["id","type","typeLabel","title","status","date","year","hours","detail","verifyUrl","verifyCode","expiresAt","courseId"]},"MeTimelineItem":{"type":"object","properties":{"event_type":{"type":"string","description":"\"course_started\", \"lesson_completed\", \"course_completed\", \"certificate_earned\", \"badge_earned\" and any later event kind."},"title":{"type":"string","description":"What it was about."},"detail":{"type":["string","null"],"description":"Score or verification code, where relevant."},"occurred_at":{"type":"string","format":"date-time","description":"When it happened."},"meta":{"type":"object","description":"Identifiers: course_id, lesson_id, certificate_id, badge_id ..."}},"required":["event_type","title","detail","occurred_at","meta"]},"MeTranscriptItem":{"type":"object","properties":{"completed":{"type":"array","description":"Completed record rows: id, type, title, status, date, year, hours, detail, verifyUrl, verifyCode, expiresAt, courseId."},"credits":{"type":"object","description":"{ awards: [...], totals: [{ body, unit, year, credits }] }. Empty when accreditation is not set up."}},"required":["completed","credits"]},"OrgDepartmentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Department id."},"name":{"type":"string","description":"Department name."},"slug":{"type":"string","description":"URL-safe identifier."},"description":{"type":["string","null"],"description":"Department description."},"parent_id":{"type":["string","null"],"format":"uuid","description":"Parent department id, or null at the root."},"type":{"type":"string","description":"Org-unit type, e.g. \"department\"."},"color":{"type":"string","description":"Display colour, hex."},"created_at":{"type":"string","format":"date-time","description":"When the department was created."}},"required":["id","name","slug","description","parent_id","type","color","created_at"]},"OrgGroupsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Group id — pass to POST /api/v1/org/groups/{id}/members."},"name":{"type":"string","description":"Group name."},"description":{"type":"string","description":"Group description."},"is_dynamic":{"type":"boolean","description":"True when membership is computed from `rule`, not stored — POST .../members refuses these."},"owner_department_id":{"type":["string","null"],"format":"uuid","description":"Department this group belongs to, if scoped to one."},"created_at":{"type":"string","format":"date-time","description":"When the group was created."}},"required":["id","name","description","is_dynamic","owner_department_id","created_at"]},"OrgGroupsIdMembersItem":{"type":"object","properties":{"group_id":{"type":"string","format":"uuid","description":"The group."},"user_id":{"type":"string","format":"uuid","description":"The person added."},"added_at":{"type":"string","format":"date-time","description":"When the membership was recorded (already existing membership keeps its original added_at — this is an upsert, not always a fresh row)."}},"required":["group_id","user_id","added_at"]},"OrgUsersItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"User id (matches auth.users.id, but that table is never joined here)."},"full_name":{"type":"string","description":"Display name."},"title":{"type":"string","description":"Job title text."},"department_id":{"type":["string","null"],"format":"uuid","description":"Department this member belongs to."},"job_title_id":{"type":["string","null"],"format":"uuid","description":"Structured job-title reference, if set."},"manager_id":{"type":["string","null"],"format":"uuid","description":"This member's manager (another user_id)."},"timezone":{"type":["string","null"],"description":"IANA timezone, if set."},"created_at":{"type":"string","format":"date-time","description":"When the profile was created."},"updated_at":{"type":"string","format":"date-time","description":"When the profile was last updated."}},"required":["user_id","full_name","title","department_id","job_title_id","manager_id","timezone","created_at","updated_at"]},"OrgUsersPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Invitation id."},"email":{"type":"string","description":"Invited address."},"role_key":{"type":"string","description":"Role the person will hold once they accept."},"expires_at":{"type":"string","format":"date-time","description":"When the invitation link stops working (7 days)."},"accept_url":{"type":"string","description":"The link the invited person opens to accept."},"email_sent":{"type":"boolean","description":"Whether the invitation email was actually sent (false only if the mail provider failed)."}},"required":["id","email","role_key","expires_at","accept_url","email_sent"]},"PartnerAi-keysItem":{"type":"object","properties":{"provider":{"type":"string","description":"The provider removed."},"module_key":{"type":["string","null"],"description":"The module the key was limited to, or null."},"deleted":{"type":"boolean","description":"Always true."}},"required":["provider","module_key","deleted"]},"PartnerAi-keysGetItem":{"type":"object","properties":{"keys":{"type":"array","description":"One object per key: `provider`, `module_key`, `key_hint`, `base_url`, `config`, `label`, `is_active`, `inherit_to_children`, `allowed_child_tenants`, `created_at`, `updated_at`."}},"required":["keys"]},"PartnerAi-keysPutItem":{"type":"object","properties":{"provider":{"type":"string","description":"The provider: openrouter, openai, anthropic, azure_openai, google."},"module_key":{"type":["string","null"],"description":"The module the key is limited to (for example `module.ai`); null is the default for the provider and is used where no module-specific key exists."},"key_hint":{"type":"string","description":"The last characters of the stored key, so you can tell which one it is. The key itself is never returned."},"base_url":{"type":"string","description":"The provider endpoint (required for azure_openai; otherwise the provider default when empty)."},"config":{"type":"object","description":"Provider extras, for example the azure_openai `apiVersion`."},"label":{"type":"string","description":"A name for your own reference."},"is_active":{"type":"boolean","description":"Whether the key is used."},"inherit_to_children":{"type":"boolean","description":"Whether child organisations run on this key when they have none of their own."},"allowed_child_tenants":{"type":["array","null"],"description":"Child organisation ids that may use it; null means every child."},"created_at":{"type":"string","format":"date-time","description":"When it was first stored."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["provider","module_key","key_hint","base_url","config","label","is_active","inherit_to_children","allowed_child_tenants","created_at","updated_at"]},"PartnerContent-providersItem":{"type":"object","properties":{"provider_key":{"type":"string","description":"The provider removed."},"deleted":{"type":"boolean","description":"Always true."}},"required":["provider_key","deleted"]},"PartnerContent-providersGetItem":{"type":"object","properties":{"providers":{"type":"array","description":"One object per provider: `provider_key`, `inherit_to_children`, `allowed_child_tenants`, `rights_note`, `created_at`, `updated_at`."}},"required":["providers"]},"PartnerContent-providersPutItem":{"type":"object","properties":{"provider_key":{"type":"string","description":"The content provider key (e.g. `go1`)."},"inherit_to_children":{"type":"boolean","description":"Whether child organisations may use this credential."},"allowed_child_tenants":{"type":["array","null"],"description":"Child organisation ids that may inherit it; null means every child."},"rights_note":{"type":"string","description":"The contractual basis for sharing the credential."},"created_at":{"type":"string","format":"date-time","description":"When it was first stored."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["provider_key","inherit_to_children","allowed_child_tenants","rights_note","created_at","updated_at"]},"PartnerMeItem":{"type":"object","properties":{"partner_id":{"type":"string","format":"uuid","description":"The partner id."},"slug":{"type":"string","description":"The partner slug (immutable)."},"name":{"type":"string","description":"The partner display name."},"tier":{"type":"string","enum":["registered","integrated","oem"],"description":"The partner tier."},"status":{"type":"string","enum":["active","suspended"],"description":"The partner status."},"region":{"type":"string","description":"The partner region."},"environment":{"type":"string","enum":["sandbox","live"],"description":"sandbox (free, test customers and test keys only, never billed) until go-live is approved, then live."},"key_environment":{"type":"string","enum":["live","test"],"description":"Whether this key is a test (pak_test_) or live key."},"home_tenant_id":{"type":"string","format":"uuid","description":"The partner's own organisation."},"contract":{"type":"object","description":"The commercial ceilings: modules, max_child_tenants, max_learners_per_tenant, api_rpm, identity_mode, support_model."},"role":{"type":"string","enum":["owner","admin","support","viewer"],"description":"The partner role of the person this key acts as, now."},"scopes":{"type":"array","description":"The scopes on this key."},"wallet_balance":{"type":["number","null"],"description":"Partner credit wallet balance (undiscounted credits). Null only if the balance could not be read."}},"required":["partner_id","slug","name","tier","status","region","environment","key_environment","home_tenant_id","contract","role","scopes","wallet_balance"]},"PartnerRevenue-reportsItem":{"type":"object","properties":{"period":{"type":"string","format":"date","description":"First day of the month recorded."},"tenant_id":{"type":["string","null"],"format":"uuid","description":"The organisation, or null for the partner total."},"revenue_minor":{"type":"integer","description":"The figure now on record."},"currency":{"type":"string","description":"Its currency."},"statement_status":{"type":["string","null"],"description":"Status of that month's statement if it was recomputed."}},"required":["period","tenant_id","revenue_minor","currency","statement_status"]},"PartnerStatementsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The statement id."},"period_start":{"type":"string","format":"date","description":"First day of the month covered."},"period_end":{"type":"string","format":"date","description":"Last day of the month covered."},"status":{"type":"string","enum":["issued","paid","void","awaiting_revenue_report"],"description":"Where the statement is."},"currency":{"type":"string","description":"INR or USD."},"total_minor":{"type":"integer","description":"The amount due, minor units."},"lines":{"type":"array","description":"Each line: `code`, `description`, `quantity`, `unit_minor`, `amount_minor`."},"flags":{"type":"array","description":"Notes such as `revenue_proxy_used`, `emandate_limit_invoice`."},"payment_method":{"type":"string","enum":["invoice","auto_charge","manual"],"description":"How it is collected."},"invoice_url":{"type":["string","null"],"description":"The Razorpay pay link, when collected by invoice."},"due_at":{"type":["string","null"],"format":"date-time","description":"Payment due date."},"generated_at":{"type":"string","format":"date-time","description":"When it was computed."},"issued_at":{"type":["string","null"],"format":"date-time","description":"When it was issued."},"paid_at":{"type":["string","null"],"format":"date-time","description":"When payment was received."}},"required":["id","period_start","period_end","status","currency","total_minor","lines","flags","payment_method","invoice_url","due_at","generated_at","issued_at","paid_at"]},"PartnerStatementsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The statement id."},"period_start":{"type":"string","format":"date","description":"First day of the month covered."},"period_end":{"type":"string","format":"date","description":"Last day of the month covered."},"status":{"type":"string","enum":["issued","paid","void","awaiting_revenue_report"],"description":"Where the statement is."},"currency":{"type":"string","description":"INR or USD."},"total_minor":{"type":"integer","description":"The amount due, minor units."},"lines":{"type":"array","description":"Each line: `code`, `description`, `quantity`, `unit_minor`, `amount_minor`."},"flags":{"type":"array","description":"Notes such as `revenue_proxy_used`, `emandate_limit_invoice`."},"invoice_url":{"type":["string","null"],"description":"The Razorpay pay link, when collected by invoice."}},"required":["id","period_start","period_end","status","currency","total_minor","lines","flags","invoice_url"]},"PartnerSupportTicketsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The ticket id."},"tenant_id":{"type":"string","format":"uuid","description":"The customer organisation the ticket is about."},"partner_id":{"type":"string","format":"uuid","description":"The owning partner."},"origin":{"type":"string","enum":["customer","partner"],"description":"Who opened it."},"severity":{"type":"string","enum":["low","normal","high","urgent"],"description":"Severity."},"subject":{"type":"string","description":"Subject line."},"body":{"type":"string","description":"Description."},"status":{"type":"string","enum":["open","in_progress","waiting_on_partner","resolved","closed"],"description":"Current status."},"external_ref":{"type":["string","null"],"description":"The partner's own reference for the ticket."},"created_at":{"type":"string","format":"date-time","description":"When it was opened."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."},"handled_by":{"type":"string","enum":["partner","platform"],"description":"Who works the ticket. Always `partner` in this list: tickets Personal Academy handles for your customers (security and grievance) are not listed."},"category":{"type":"string","enum":["how-to","billing","security","grievance","bug","outage","data-request"],"description":"What the ticket is about. Tickets you open are `how-to`."},"channel":{"type":"string","enum":["app","guide","email","partner_console","partner_api","staff","shared_channel","phone"],"description":"How the ticket reached support: `app` or `guide` when your customer opened it, `partner_api` or `partner_console` when you did."},"due_at":{"type":["string","null"],"format":"date-time","description":"The Personal Academy response target, when one applies."},"first_response_at":{"type":["string","null"],"format":"date-time","description":"The first reply from the side that handles the ticket."},"resolved_at":{"type":["string","null"],"format":"date-time","description":"When it was resolved or closed; cleared if it is reopened."},"escalated_to_platform":{"type":"boolean","description":"True once the ticket has been handed to Personal Academy support (L2)."},"escalated_at":{"type":["string","null"],"format":"date-time","description":"When it was handed to Personal Academy support."}},"required":["id","tenant_id","partner_id","origin","severity","subject","body","status","external_ref","created_at","updated_at","handled_by","category","channel","due_at","first_response_at","resolved_at","escalated_to_platform","escalated_at"]},"PartnerSupportTicketsPostItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The ticket id."}},"required":["id"]},"PartnerTenantsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The organisation id."},"partner_id":{"type":"string","format":"uuid","description":"The owning partner."},"name":{"type":"string","description":"Organisation name."},"slug":{"type":"string","description":"The subdomain."},"status":{"type":"string","enum":["active","suspended","deleted"],"description":"Lifecycle state."},"plan":{"type":["string","null"],"description":"Deprecated: the legacy plan key, null for an organisation created on a tier."},"active_learners_30d":{"type":"integer","description":"Learners active in the last 30 days."},"learners_total":{"type":"integer","description":"Active learners."},"created_at":{"type":"string","format":"date-time","description":"When it was provisioned."}},"required":["id","partner_id","name","slug","status","plan","active_learners_30d","learners_total","created_at"]},"PartnerTenantsPostItem":{"type":"object","properties":{"tenant_id":{"type":"string","format":"uuid","description":"The new organisation."},"domain":{"type":["object","null"],"description":"Present when `domain` was sent: `{ hostname, verification_token }` or `{ hostname, error }`."},"products":{"type":["object","null"],"description":"The product switch: `{ products, enabled, disabled, unreachable }` (as PUT /api/v1/partner/tenants/{id}/products returns), `{ error, code }` when it was refused, or null when no products were named and the partner has no default set."}},"required":["tenant_id","domain","products"]},"PartnerTenantsIdBrandingItem":{"type":"object","properties":{"tenant_id":{"type":"string","format":"uuid","description":"The organisation."},"updated":{"type":"array","description":"Keys that were set."},"reset":{"type":"array","description":"Keys reset to their default."}},"required":["tenant_id","updated","reset"]},"PartnerTenantsIdCatalogueItemsItemIdAddItem":{"type":"object","properties":{"course_id":{"type":"string","format":"uuid","description":"The new draft course."},"licence_source":{"type":"string","enum":["tenant","partner"],"description":"Whose licence the item was added under."}},"required":["course_id","licence_source"]},"PartnerTenantsIdCatalogueSearchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Item id; pass to the add endpoint."},"provider_key":{"type":"string","description":"The provider."},"provider_name":{"type":"string","description":"The provider's display name."},"licence_source":{"type":"string","enum":["tenant","partner"],"description":"Whose licence applies."},"title":{"type":"string","description":"Item title."},"description":{"type":["string","null"],"description":"Item description."},"url":{"type":"string","description":"Where the item is hosted."},"image_url":{"type":["string","null"],"description":"Thumbnail."},"duration_minutes":{"type":["integer","null"],"description":"Length, when the provider states it."},"language":{"type":["string","null"],"description":"Language code."},"licence_bucket":{"type":["string","null"],"description":"Licence bucket A-D."}},"required":["id","provider_key","provider_name","licence_source","title","description","url","image_url","duration_minutes","language","licence_bucket"]},"PartnerTenantsIdChatInstall-linksItem":{"type":"object","properties":{"platform":{"type":"string","description":"The platform the link installs."},"install_url":{"type":"string","description":"The link to hand to the customer's chat administrator. Treat it as a secret until it is used."},"expires_at":{"type":"string","format":"date-time","description":"When the link stops working."}},"required":["platform","install_url","expires_at"]},"PartnerTenantsIdChatInstallationsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The installation id."},"platform":{"type":"string","description":"`slack`, `teams`, or another chat platform the organisation connected itself."},"display_name":{"type":["string","null"],"description":"The workspace or Microsoft 365 organisation name."},"is_enterprise_install":{"type":"boolean","description":"True for a Slack Enterprise Grid org-wide install."},"status":{"type":"string","description":"`active` or `disabled`."},"installed_at":{"type":"string","format":"date-time","description":"When it was connected."},"last_event_at":{"type":["string","null"],"format":"date-time","description":"When the bot last heard from the workspace."},"has_error":{"type":"boolean","description":"True when the last delivery failed."}},"required":["id","platform","display_name","is_enterprise_install","status","installed_at","last_event_at","has_error"]},"PartnerTenantsIdDomainsItem":{"type":"object","properties":{"hostname":{"type":"string","description":"The claimed hostname."},"verification_token":{"type":"string","description":"The CNAME target the hostname must point at before it verifies."},"verified":{"type":"boolean","description":"Always false at claim time."}},"required":["hostname","verification_token","verified"]},"PartnerTenantsIdIntegrationsContent-providersItem":{"type":"object","properties":{"provider_key":{"type":"string","description":"The provider."},"deleted":{"type":"boolean","description":"Always true."}},"required":["provider_key","deleted"]},"PartnerTenantsIdIntegrationsContent-providersGetItem":{"type":"object","properties":{"providers":{"type":"array","description":"One object per provider: `provider_key`, `enabled`, `credential_mode`, `credential_hint`."}},"required":["providers"]},"PartnerTenantsIdIntegrationsContent-providersPutItem":{"type":"object","properties":{"provider_key":{"type":"string","description":"The content provider key (e.g. `go1`)."},"enabled":{"type":"boolean","description":"Whether the organisation uses this provider."},"credential_mode":{"type":"string","enum":["tenant","platform","none"],"description":"`tenant` = the organisation's own account; `none` = no own account (a partner credential may apply)."},"credential_hint":{"type":["string","null"],"description":"A non-secret hint of the stored credential (e.g. `api_key=…ab12`); null when none is stored."}},"required":["provider_key","enabled","credential_mode","credential_hint"]},"PartnerTenantsIdIntegrationsHrisItem":{"type":"object","properties":{"provider":{"type":"string","description":"The provider removed."},"deleted":{"type":"boolean","description":"Always true."}},"required":["provider","deleted"]},"PartnerTenantsIdIntegrationsHrisGetItem":{"type":"object","properties":{"connections":{"type":"array","description":"One object per connection: `id`, `provider`, `display_name`, `identity_mode`, `rules`, `status`, `schedule`, `last_sync_at`, `last_error`."},"providers":{"type":"array","description":"The HRIS providers available: `id`, `label`."}},"required":["connections","providers"]},"PartnerTenantsIdIntegrationsHrisPutItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The connection."},"provider":{"type":"string","description":"The HRIS adapter key (e.g. `darwinbox`)."},"display_name":{"type":"string","description":"Name shown to the organisation's admins."},"identity_mode":{"type":"string","enum":["pseudonymous","email_allowed"],"description":"Whether synced people may carry an email address."},"rules":{"type":"object","description":"Sync rules (departments, leavers, safety limits)."},"status":{"type":"string","enum":["disabled","active","error"],"description":"Whether scheduled syncs run."},"schedule":{"type":"string","enum":["hourly","daily","manual"],"description":"How often the sync runs."},"last_sync_at":{"type":["string","null"],"format":"date-time","description":"When it last synced."},"last_error":{"type":["string","null"],"description":"The last sync error, if any."}},"required":["id","provider","display_name","identity_mode","rules","status","schedule","last_sync_at","last_error"]},"PartnerTenantsIdKeysItem":{"type":"object","properties":{"key_id":{"type":"string","format":"uuid","description":"The key id (use it to revoke)."},"secret":{"type":"string","description":"The raw key. Shown once; only its hash is stored."},"key_prefix":{"type":"string","description":"Display prefix, safe to log."},"environment":{"type":"string","enum":["live","test"],"description":"test for a test (sandbox) customer: the secret starts pak_test_ and reaches only that customer."},"scopes":{"type":"array","description":"Scopes on the key."},"permissions":{"type":"array","description":"Permissions on the key."},"expires_at":{"type":["string","null"],"format":"date-time","description":"Expiry, if any."}},"required":["key_id","secret","key_prefix","environment","scopes","permissions","expires_at"]},"PartnerTenantsIdKeysKeyIdItem":{"type":"object","properties":{"key_id":{"type":"string","format":"uuid","description":"The revoked key."},"revoked":{"type":"boolean","description":"Always true."}},"required":["key_id","revoked"]},"PartnerTenantsIdLearnersItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"The learner's user id."},"created":{"type":"boolean","description":"True when this call created the learner."}},"required":["user_id","created"]},"PartnerTenantsIdLearnersExternal_idItem":{"type":"object","properties":{"external_id":{"type":"string","description":"The identifier that was erased."},"erased":{"type":"boolean","description":"True when the erasure ran."},"detail":{"type":"object","description":"What the erasure removed (counts)."},"auth_retired":{"type":["boolean","null"],"description":"Whether the pseudonymous sign-in identity was retired as well."}},"required":["external_id","erased","detail","auth_retired"]},"PartnerTenantsIdLearnersExternal_idPatchItem":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid","description":"The learner's user id."},"external_id":{"type":"string","description":"The identifier used in the path."},"active":{"type":"boolean","description":"Whether the learner is active after the update."},"updated":{"type":"array","description":"The fields that were changed."}},"required":["user_id","external_id","active","updated"]},"PartnerTenantsIdProductsItem":{"type":"object","properties":{"tenant_id":{"type":"string","format":"uuid","description":"The organisation."},"products":{"type":"array","description":"Per product: `{ product, name, unit, on, reachable, needsTier, modules }`. `reachable` is false when the product is on but the organisation's tier does not include all of its modules (a switch never raises a tier)."},"defaults":{"type":"array","description":"The partner-wide default product keys."}},"required":["tenant_id","products","defaults"]},"PartnerTenantsIdProductsPutItem":{"type":"object","properties":{"tenant_id":{"type":"string","format":"uuid","description":"The organisation."},"products":{"type":"array","description":"Per product: `{ product, name, unit, on, reachable, needsTier, modules }`. `reachable` is false when the product is on but the organisation's tier does not include all of its modules (a switch never raises a tier)."},"enabled":{"type":"array","description":"Products switched on by this request."},"disabled":{"type":"array","description":"Products switched off by this request."},"unreachable":{"type":"array","description":"Products that are on but that the organisation's tier does not reach yet."}},"required":["tenant_id","products","enabled","disabled","unreachable"]},"PartnerUsageItem":{"type":"object","properties":{"from":{"type":"string","format":"date","description":"The first day covered."},"to":{"type":"string","format":"date","description":"The last day covered."},"group_by":{"type":"string","enum":["tenant","feature","key"],"description":"The grouping used."},"rows":{"type":"array","description":"One object per group: `key`, `ai_credits`, `api_calls`, `embed_sessions`, `lti_launches`, `active_learner_days`."},"totals":{"type":"object","description":"The same counters summed over every row."}},"required":["from","to","group_by","rows","totals"]},"PartnerWalletTopupsItem":{"type":"object","properties":{"topup_id":{"type":"string","format":"uuid","description":"The top-up."},"status":{"type":"string","enum":["pending","paid","manual"],"description":"Where it stands."},"credits":{"type":"integer","description":"Credits this amount buys."},"invoice_url":{"type":["string","null"],"description":"Pay link when `status` is `pending`."}},"required":["topup_id","status","credits","invoice_url"]},"PathsIdProgressItem":{"type":"object","properties":{"path":{"type":"object","description":"id, title, description, thumbnailUrl, coverImageUrl, isPublic."},"enrolment":{"type":["object","null"],"description":"id, dueAt, completedAt, assignedBy ({ id, name }) or null."},"steps":{"type":"array","description":"Ordered steps: course (id, title, ...), order, required, milestone, state, unlocksNext."},"progressPct":{"type":"integer","description":"Share of steps done, 0..100."},"nextStepId":{"type":["string","null"],"format":"uuid","description":"Course id of the first step not yet done."}},"required":["path","enrolment","steps","progressPct","nextStepId"]},"PathsEnrolmentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Enrolment id."},"user_id":{"type":"string","format":"uuid","description":"Enrolled learner."},"path_id":{"type":"string","format":"uuid","description":"Learning path."},"enrolled_at":{"type":"string","format":"date-time","description":"When enrolment started."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the path was completed, if it has been."},"due_at":{"type":["string","null"],"format":"date-time","description":"Assignment due date, if set."},"assignment_source":{"type":"string","description":"\"self\" or how the enrolment was assigned."}},"required":["id","user_id","path_id","enrolled_at","completed_at","due_at","assignment_source"]},"PathsEnrolmentsSelfItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Enrolment id."},"path_id":{"type":"string","format":"uuid","description":"Learning path."},"enrolled_at":{"type":"string","format":"date-time","description":"When the enrolment started."},"due_at":{"type":["string","null"],"format":"date-time","description":"Assignment due date, if set."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the path was completed, if it has been."},"assignment_source":{"type":"string","description":"`self` or how the enrolment was assigned."}},"required":["id","path_id","enrolled_at","due_at","completed_at","assignment_source"]},"PathsPathsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Learning path id."},"title":{"type":"string","description":"Path title."},"description":{"type":["string","null"],"description":"Path description."},"slug":{"type":["string","null"],"description":"URL-safe identifier."},"is_public":{"type":"boolean","description":"Whether the path is publicly listed."},"thumbnail_url":{"type":["string","null"],"description":"Cover image URL."},"created_at":{"type":"string","format":"date-time","description":"When the path was created."},"updated_at":{"type":"string","format":"date-time","description":"When the path was last updated."}},"required":["id","title","description","slug","is_public","thumbnail_url","created_at","updated_at"]},"PodcastsEpisodesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Episode id."},"feed_id":{"type":"string","format":"uuid","description":"Its feed."},"feed_title":{"type":["string","null"],"description":"Feed title."},"title":{"type":"string","description":"Episode title."},"description":{"type":["string","null"],"description":"Show notes."},"audio_url":{"type":"string","description":"The audio file URL."},"duration_seconds":{"type":["integer","null"],"description":"Length, when the feed states it."},"published_at":{"type":["string","null"],"format":"date-time","description":"When it was published."},"position_seconds":{"type":"integer","description":"Where the learner stopped (0 when they have not started)."},"completed":{"type":"boolean","description":"Whether the learner finished it."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When they finished it."}},"required":["id","feed_id","feed_title","title","description","audio_url","duration_seconds","published_at","position_seconds","completed","completed_at"]},"PodcastsEpisodesIdProgressItem":{"type":"object","properties":{"episode_id":{"type":"string","format":"uuid","description":"The episode."},"position_seconds":{"type":"integer","description":"Stored position."},"completed":{"type":"boolean","description":"Whether it is marked finished."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When it was marked finished."},"updated_at":{"type":"string","format":"date-time","description":"When this progress was last written."}},"required":["episode_id","position_seconds","completed","completed_at","updated_at"]},"PodcastsFeedsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Feed id."},"title":{"type":"string","description":"Feed title."},"description":{"type":["string","null"],"description":"What the podcast is about."},"image_url":{"type":["string","null"],"description":"Cover image."},"last_fetched_at":{"type":["string","null"],"format":"date-time","description":"When episodes were last refreshed from the feed."},"subscribed":{"type":"boolean","description":"Whether the learner follows this feed."}},"required":["id","title","description","image_url","last_fetched_at","subscribed"]},"PodcastsFeedsIdSubscribeItem":{"type":"object","properties":{"feed_id":{"type":"string","format":"uuid","description":"The feed."},"subscribed":{"type":"boolean","description":"Whether the learner now follows it."}},"required":["feed_id","subscribed"]},"ProgrammesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Programme enrolment id."},"template_id":{"type":"string","format":"uuid","description":"The programme template."},"name":{"type":"string","description":"Programme name."},"description":{"type":["string","null"],"description":"Programme description."},"category":{"type":["string","null"],"description":"Programme category."},"cover_image_url":{"type":["string","null"],"description":"Cover image."},"start_date":{"type":"string","format":"date","description":"When the programme started for the learner."},"status":{"type":"string","description":"Enrolment status."},"completed_at":{"type":["string","null"],"format":"date-time","description":"When it was completed, if it has been."}},"required":["id","template_id","name","description","category","cover_image_url","start_date","status","completed_at"]},"ProgrammesIdItem":{"type":"object","properties":{"programme":{"type":"object","description":"enrolmentId, templateId, name, description, category, coverImageUrl, startDate, status, completedAt, assignedBy."},"steps":{"type":"array","description":"Ordered steps: instanceId, order, stepType, required, milestone, title, dueAt, state, session, observation."},"progressPct":{"type":"integer","description":"Share of required steps done, 0..100."}},"required":["programme","steps","progressPct"]},"ProgrammesEnrolmentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Programme enrolment id."},"template_id":{"type":"string","format":"uuid","description":"The programme template."},"user_id":{"type":"string","format":"uuid","description":"The enrolled person."},"start_date":{"type":"string","format":"date","description":"Programme start."},"status":{"type":"string","description":"Enrolment status."}},"required":["id","template_id","user_id","start_date","status"]},"SkillsCompetenciesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Competency id."},"name":{"type":"string","description":"Competency name."},"description":{"type":"string","description":"Competency description."},"category":{"type":"string","description":"e.g. \"functional\", \"leadership\"."},"max_level":{"type":"integer","description":"Highest level this competency can be scored at."},"created_at":{"type":"string","format":"date-time","description":"When the competency was created."},"updated_at":{"type":"string","format":"date-time","description":"When the competency was last updated."}},"required":["id","name","description","category","max_level","created_at","updated_at"]},"SkillsGapsItem":{"type":"object","properties":{"competency_id":{"type":"string","format":"uuid","description":"Competency id."},"competency_name":{"type":"string","description":"Competency name."},"category":{"type":"string","description":"Category."},"expected_level":{"type":"integer","description":"Level the role expects."},"actual_level":{"type":["integer","null"],"description":"Best evidence of the learner's level."},"actual_source":{"type":"string","enum":["verified","self_assessed","none"],"description":"Where actual_level came from."},"gap":{"type":"integer","description":"expected minus actual; positive means a gap."},"recommended_courses":{"type":"array","description":"Courses that build the competency: { id, title }."}},"required":["competency_id","competency_name","category","expected_level","actual_level","actual_source","gap","recommended_courses"]},"SkillsScoresItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Score record id."},"user_id":{"type":"string","format":"uuid","description":"Learner this score belongs to."},"skill_id":{"type":"string","format":"uuid","description":"Skill being scored."},"score":{"type":"integer","description":"Current score."},"updated_at":{"type":"string","format":"date-time","description":"When the score last changed."}},"required":["id","user_id","skill_id","score","updated_at"]},"SkillsSelf-assessmentsItem":{"type":"object","properties":{"competency_id":{"type":"string","format":"uuid","description":"Competency id."},"name":{"type":"string","description":"Competency name."},"description":{"type":"string","description":"What it covers."},"category":{"type":"string","description":"Category."},"max_level":{"type":"integer","description":"Highest level (levels run 0 to max_level)."},"expected_level":{"type":["integer","null"],"description":"Level the learner's job title expects, if any."},"self_level":{"type":["integer","null"],"description":"The learner's own rating, null until assessed."},"notes":{"type":["string","null"],"description":"The learner's notes."},"assessed_at":{"type":["string","null"],"format":"date-time","description":"When they last assessed."},"previous_level":{"type":["integer","null"],"description":"The rating before the current one."},"levels":{"type":"array","description":"Level definitions: level_number, name, description."}},"required":["competency_id","name","description","category","max_level","expected_level","self_level","notes","assessed_at","previous_level","levels"]},"SkillsSelf-assessmentsPutItem":{"type":"object","properties":{"saved":{"type":"array","description":"One entry per saved item: { competency_id, self_level }."}},"required":["saved"]},"SkillsSkillsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Skill id."},"name":{"type":"string","description":"Skill name."},"description":{"type":["string","null"],"description":"Skill description."},"parent_skill_id":{"type":["string","null"],"format":"uuid","description":"Parent skill, for a skill hierarchy."},"external_source":{"type":["string","null"],"description":"Where this skill was imported from, if external."},"external_id":{"type":["string","null"],"description":"Id in the external source, if imported."},"created_at":{"type":"string","format":"date-time","description":"When the skill was created."}},"required":["id","name","description","parent_skill_id","external_source","external_id","created_at"]},"SocialCourse-discussionsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Thread or reply id."},"body":{"type":"string","description":"Text."},"lessonId":{"type":["string","null"],"format":"uuid","description":"The lesson the thread is attached to, or null for the course."},"createdAt":{"type":"string","format":"date-time","description":"When it was written."},"updatedAt":{"type":"string","format":"date-time","description":"When it was last changed."},"isPinned":{"type":"boolean","description":"Pinned by a moderator."},"isAcceptedAnswer":{"type":"boolean","description":"Marked as the accepted answer."},"isLocked":{"type":"boolean","description":"Locked by a moderator: no replies."},"upvotes":{"type":"integer","description":"Upvotes, counted live."},"upvoted":{"type":"boolean","description":"Whether the learner upvoted it."},"author":{"type":"object","description":"`{ id, name, avatarUrl }`."},"isCourseAuthor":{"type":"boolean","description":"Written by the course author."},"isInstructor":{"type":"boolean","description":"Written by the course instructor."},"isMine":{"type":"boolean","description":"Written by the learner."},"replies":{"type":"array","description":"Nested replies, same shape."}},"required":["id","body","lessonId","createdAt","updatedAt","isPinned","isAcceptedAnswer","isLocked","upvotes","upvoted","author","isCourseAuthor","isInstructor","isMine","replies"]},"SocialFeedItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Post id."},"type":{"type":"string","enum":["idea","post","video_demo","announcement"],"description":"Kind of post."},"title":{"type":"string","description":"Title (may be empty)."},"body":{"type":"string","description":"Post text."},"media_url":{"type":["string","null"],"description":"Video link for a video_demo."},"visibility":{"type":"string","enum":["org","department","group","roles","space"],"description":"Audience bucket. Who can actually see it is decided by row-level security for the learner the call acts as."},"status":{"type":"string","enum":["draft","published","archived","hidden"],"description":"Publication state. Lists return published posts only."},"space_id":{"type":["string","null"],"format":"uuid","description":"The space, for a space post."},"space_name":{"type":["string","null"],"description":"Name of that space."},"department_id":{"type":["string","null"],"format":"uuid","description":"First targeted department, for a department post."},"department_name":{"type":["string","null"],"description":"Name of that department."},"author":{"type":"object","description":"Who wrote it: `{ id, name, title, avatar_url }`. `id` is the author's user id."},"tags":{"type":"array","description":"Tag names."},"is_feedback_requested":{"type":"boolean","description":"The author asked for feedback."},"is_pinned":{"type":"boolean","description":"Pinned by a moderator."},"is_locked":{"type":"boolean","description":"A moderator locked the thread: no new comments."},"like_count":{"type":"integer","description":"Likes."},"clap_count":{"type":"integer","description":"Claps."},"insightful_count":{"type":"integer","description":"Insightful reactions."},"question_count":{"type":"integer","description":"Question reactions."},"comment_count":{"type":"integer","description":"Comments, replies included."},"share_count":{"type":"integer","description":"Shares."},"view_count":{"type":"integer","description":"Views."},"viewer_reaction":{"type":["string","null"],"enum":["like","clap","insightful","question"],"description":"The reaction the learner holds on this post, if any."},"viewer_saved":{"type":"boolean","description":"Whether the learner saved this post."},"hot_score":{"type":["number","null"],"description":"The hot-ranking score; present only on a `sort=hot` feed."},"published_at":{"type":"string","format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","type","title","body","media_url","visibility","status","space_id","space_name","department_id","department_name","author","tags","is_feedback_requested","is_pinned","is_locked","like_count","clap_count","insightful_count","question_count","comment_count","share_count","view_count","viewer_reaction","viewer_saved","hot_score","published_at","created_at","updated_at"]},"SocialModerationPostIdItem":{"type":"object","properties":{"ok":{"type":"boolean","description":"Always true on success."},"action_id":{"type":"string","format":"uuid","description":"The recorded moderation action."},"action":{"type":"string","description":"The action applied."},"content_type":{"type":"string","description":"What was moderated."},"content_id":{"type":"string","format":"uuid","description":"Its id."}},"required":["ok","action_id","action","content_type","content_id"]},"SocialModerationQueueItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Report id."},"content_type":{"type":"string","enum":["post","post_comment","course_discussion"],"description":"What was reported."},"content_id":{"type":"string","format":"uuid","description":"The reported content. For a post, the id to pass to POST /social/moderation/{postId}."},"reason":{"type":"string","description":"The reporter's reason."},"detail":{"type":"string","description":"The reporter's explanation."},"status":{"type":"string","enum":["open","upheld","dismissed"],"description":"Report state."},"reporter_id":{"type":["string","null"],"format":"uuid","description":"Who reported."},"reporter_name":{"type":["string","null"],"description":"Their name."},"report_count":{"type":"integer","description":"Open reports against the same content."},"content":{"type":"object","description":"`{ found, excerpt, author_id, author_name, space_id, is_hidden, is_locked, is_pinned }`."},"created_at":{"type":"string","format":"date-time","description":"When it was reported."},"resolved_at":{"type":["string","null"],"format":"date-time","description":"When it was resolved."},"resolved_by":{"type":["string","null"],"format":"uuid","description":"Who resolved it."},"resolution_note":{"type":"string","description":"The resolver's note."}},"required":["id","content_type","content_id","reason","detail","status","reporter_id","reporter_name","report_count","content","created_at","resolved_at","resolved_by","resolution_note"]},"SocialPostsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Post id."},"type":{"type":"string","enum":["idea","post","video_demo","announcement"],"description":"Kind of post."},"title":{"type":"string","description":"Title (may be empty)."},"body":{"type":"string","description":"Post text."},"media_url":{"type":["string","null"],"description":"Video link for a video_demo."},"visibility":{"type":"string","enum":["org","department","group","roles","space"],"description":"Audience bucket. Who can actually see it is decided by row-level security for the learner the call acts as."},"status":{"type":"string","enum":["draft","published","archived","hidden"],"description":"Publication state. Lists return published posts only."},"space_id":{"type":["string","null"],"format":"uuid","description":"The space, for a space post."},"space_name":{"type":["string","null"],"description":"Name of that space."},"department_id":{"type":["string","null"],"format":"uuid","description":"First targeted department, for a department post."},"department_name":{"type":["string","null"],"description":"Name of that department."},"author":{"type":"object","description":"Who wrote it: `{ id, name, title, avatar_url }`. `id` is the author's user id."},"tags":{"type":"array","description":"Tag names."},"is_feedback_requested":{"type":"boolean","description":"The author asked for feedback."},"is_pinned":{"type":"boolean","description":"Pinned by a moderator."},"is_locked":{"type":"boolean","description":"A moderator locked the thread: no new comments."},"like_count":{"type":"integer","description":"Likes."},"clap_count":{"type":"integer","description":"Claps."},"insightful_count":{"type":"integer","description":"Insightful reactions."},"question_count":{"type":"integer","description":"Question reactions."},"comment_count":{"type":"integer","description":"Comments, replies included."},"share_count":{"type":"integer","description":"Shares."},"view_count":{"type":"integer","description":"Views."},"viewer_reaction":{"type":["string","null"],"enum":["like","clap","insightful","question"],"description":"The reaction the learner holds on this post, if any."},"viewer_saved":{"type":"boolean","description":"Whether the learner saved this post."},"hot_score":{"type":["number","null"],"description":"The hot-ranking score; present only on a `sort=hot` feed."},"published_at":{"type":"string","format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","type","title","body","media_url","visibility","status","space_id","space_name","department_id","department_name","author","tags","is_feedback_requested","is_pinned","is_locked","like_count","clap_count","insightful_count","question_count","comment_count","share_count","view_count","viewer_reaction","viewer_saved","hot_score","published_at","created_at","updated_at"]},"SocialPostsIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The deleted post id."}},"required":["id"]},"SocialPostsIdGetItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Post id."},"type":{"type":"string","enum":["idea","post","video_demo","announcement"],"description":"Kind of post."},"title":{"type":"string","description":"Title (may be empty)."},"body":{"type":"string","description":"Post text."},"media_url":{"type":["string","null"],"description":"Video link for a video_demo."},"visibility":{"type":"string","enum":["org","department","group","roles","space"],"description":"Audience bucket. Who can actually see it is decided by row-level security for the learner the call acts as."},"status":{"type":"string","enum":["draft","published","archived","hidden"],"description":"Publication state. Lists return published posts only."},"space_id":{"type":["string","null"],"format":"uuid","description":"The space, for a space post."},"space_name":{"type":["string","null"],"description":"Name of that space."},"department_id":{"type":["string","null"],"format":"uuid","description":"First targeted department, for a department post."},"department_name":{"type":["string","null"],"description":"Name of that department."},"author":{"type":"object","description":"Who wrote it: `{ id, name, title, avatar_url }`. `id` is the author's user id."},"tags":{"type":"array","description":"Tag names."},"is_feedback_requested":{"type":"boolean","description":"The author asked for feedback."},"is_pinned":{"type":"boolean","description":"Pinned by a moderator."},"is_locked":{"type":"boolean","description":"A moderator locked the thread: no new comments."},"like_count":{"type":"integer","description":"Likes."},"clap_count":{"type":"integer","description":"Claps."},"insightful_count":{"type":"integer","description":"Insightful reactions."},"question_count":{"type":"integer","description":"Question reactions."},"comment_count":{"type":"integer","description":"Comments, replies included."},"share_count":{"type":"integer","description":"Shares."},"view_count":{"type":"integer","description":"Views."},"viewer_reaction":{"type":["string","null"],"enum":["like","clap","insightful","question"],"description":"The reaction the learner holds on this post, if any."},"viewer_saved":{"type":"boolean","description":"Whether the learner saved this post."},"hot_score":{"type":["number","null"],"description":"The hot-ranking score; present only on a `sort=hot` feed."},"published_at":{"type":"string","format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","type","title","body","media_url","visibility","status","space_id","space_name","department_id","department_name","author","tags","is_feedback_requested","is_pinned","is_locked","like_count","clap_count","insightful_count","question_count","comment_count","share_count","view_count","viewer_reaction","viewer_saved","hot_score","published_at","created_at","updated_at"]},"SocialPostsIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Post id."},"type":{"type":"string","enum":["idea","post","video_demo","announcement"],"description":"Kind of post."},"title":{"type":"string","description":"Title (may be empty)."},"body":{"type":"string","description":"Post text."},"media_url":{"type":["string","null"],"description":"Video link for a video_demo."},"visibility":{"type":"string","enum":["org","department","group","roles","space"],"description":"Audience bucket. Who can actually see it is decided by row-level security for the learner the call acts as."},"status":{"type":"string","enum":["draft","published","archived","hidden"],"description":"Publication state. Lists return published posts only."},"space_id":{"type":["string","null"],"format":"uuid","description":"The space, for a space post."},"space_name":{"type":["string","null"],"description":"Name of that space."},"department_id":{"type":["string","null"],"format":"uuid","description":"First targeted department, for a department post."},"department_name":{"type":["string","null"],"description":"Name of that department."},"author":{"type":"object","description":"Who wrote it: `{ id, name, title, avatar_url }`. `id` is the author's user id."},"tags":{"type":"array","description":"Tag names."},"is_feedback_requested":{"type":"boolean","description":"The author asked for feedback."},"is_pinned":{"type":"boolean","description":"Pinned by a moderator."},"is_locked":{"type":"boolean","description":"A moderator locked the thread: no new comments."},"like_count":{"type":"integer","description":"Likes."},"clap_count":{"type":"integer","description":"Claps."},"insightful_count":{"type":"integer","description":"Insightful reactions."},"question_count":{"type":"integer","description":"Question reactions."},"comment_count":{"type":"integer","description":"Comments, replies included."},"share_count":{"type":"integer","description":"Shares."},"view_count":{"type":"integer","description":"Views."},"viewer_reaction":{"type":["string","null"],"enum":["like","clap","insightful","question"],"description":"The reaction the learner holds on this post, if any."},"viewer_saved":{"type":"boolean","description":"Whether the learner saved this post."},"hot_score":{"type":["number","null"],"description":"The hot-ranking score; present only on a `sort=hot` feed."},"published_at":{"type":"string","format":"date-time","description":"When it was published."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","type","title","body","media_url","visibility","status","space_id","space_name","department_id","department_name","author","tags","is_feedback_requested","is_pinned","is_locked","like_count","clap_count","insightful_count","question_count","comment_count","share_count","view_count","viewer_reaction","viewer_saved","hot_score","published_at","created_at","updated_at"]},"SocialPostsIdCommentsItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Comment id."},"post_id":{"type":"string","format":"uuid","description":"The post it belongs to."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The comment this replies to, or null for a top-level comment."},"body":{"type":"string","description":"Comment text."},"author":{"type":"object","description":"Who wrote it: `{ id, name, avatar_url }`."},"is_hidden":{"type":"boolean","description":"A moderator hid it. Only its author and moderators are ever sent a hidden comment."},"created_at":{"type":"string","format":"date-time","description":"When it was written."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","post_id","parent_id","body","author","is_hidden","created_at","updated_at"]},"SocialPostsIdCommentsCommentIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The deleted comment id."}},"required":["id"]},"SocialPostsIdCommentsCommentIdPatchItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Comment id."},"post_id":{"type":"string","format":"uuid","description":"The post it belongs to."},"parent_id":{"type":["string","null"],"format":"uuid","description":"The comment this replies to, or null for a top-level comment."},"body":{"type":"string","description":"Comment text."},"author":{"type":"object","description":"Who wrote it: `{ id, name, avatar_url }`."},"is_hidden":{"type":"boolean","description":"A moderator hid it. Only its author and moderators are ever sent a hidden comment."},"created_at":{"type":"string","format":"date-time","description":"When it was written."},"updated_at":{"type":"string","format":"date-time","description":"When it was last edited."}},"required":["id","post_id","parent_id","body","author","is_hidden","created_at","updated_at"]},"SocialPostsIdReactionsItem":{"type":"object","properties":{"reaction":{"type":["string","null"],"enum":["like","clap","insightful","question"],"description":"The reaction the learner now holds, or null."},"counts":{"type":"object","description":"The post's reaction and comment counts now: `{ like, clap, insightful, question, comment }`."}},"required":["reaction","counts"]},"SocialPostsIdReportItem":{"type":"object","properties":{"report_id":{"type":"string","format":"uuid","description":"The report."},"created":{"type":"boolean","description":"False when the learner had already reported this post."},"message":{"type":"string","description":"Human-readable outcome."}},"required":["report_id","created","message"]},"SocialPostsIdSavesItem":{"type":"object","properties":{"saved":{"type":"boolean","description":"Whether the post is now saved by the learner."}},"required":["saved"]},"SocialSpacesItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Space id."},"name":{"type":"string","description":"Space name."},"slug":{"type":"string","description":"URL slug, unique within the organisation."},"description":{"type":["string","null"],"description":"What the space is for."},"icon":{"type":"string","description":"Icon name."},"visibility":{"type":"string","enum":["open","closed"],"description":"`open`: anyone in the organisation may join. `closed`: a cohort; an owner or moderator adds members."},"archived_at":{"type":["string","null"],"format":"date-time","description":"Set when a cohort is finished: read-only."},"linked_course_id":{"type":["string","null"],"format":"uuid","description":"The course this space is the cohort channel for."},"linked_course_title":{"type":["string","null"],"description":"Title of that course."},"created_by":{"type":"string","format":"uuid","description":"The creator's user id."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"member_count":{"type":"integer","description":"Members."},"my_role":{"type":["string","null"],"enum":["member","mod","owner"],"description":"The learner's role in the space, or null when not a member."},"can_manage":{"type":"boolean","description":"Whether the learner may manage the space (add members, change roles)."},"can_self_join":{"type":"boolean","description":"Whether the learner may join it right now. Decided by the database, not re-derived."},"enrolled_in_linked_course":{"type":"boolean","description":"Whether the learner is enrolled in the linked course."}},"required":["id","name","slug","description","icon","visibility","archived_at","linked_course_id","linked_course_title","created_by","created_at","member_count","my_role","can_manage","can_self_join","enrolled_in_linked_course"]},"SocialSpacesIdItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Space id."},"name":{"type":"string","description":"Space name."},"slug":{"type":"string","description":"URL slug, unique within the organisation."},"description":{"type":["string","null"],"description":"What the space is for."},"icon":{"type":"string","description":"Icon name."},"visibility":{"type":"string","enum":["open","closed"],"description":"`open`: anyone in the organisation may join. `closed`: a cohort; an owner or moderator adds members."},"archived_at":{"type":["string","null"],"format":"date-time","description":"Set when a cohort is finished: read-only."},"linked_course_id":{"type":["string","null"],"format":"uuid","description":"The course this space is the cohort channel for."},"linked_course_title":{"type":["string","null"],"description":"Title of that course."},"created_by":{"type":"string","format":"uuid","description":"The creator's user id."},"created_at":{"type":"string","format":"date-time","description":"When it was created."},"member_count":{"type":"integer","description":"Members."},"my_role":{"type":["string","null"],"enum":["member","mod","owner"],"description":"The learner's role in the space, or null when not a member."},"can_manage":{"type":"boolean","description":"Whether the learner may manage the space (add members, change roles)."},"can_self_join":{"type":"boolean","description":"Whether the learner may join it right now. Decided by the database, not re-derived."},"enrolled_in_linked_course":{"type":"boolean","description":"Whether the learner is enrolled in the linked course."}},"required":["id","name","slug","description","icon","visibility","archived_at","linked_course_id","linked_course_title","created_by","created_at","member_count","my_role","can_manage","can_self_join","enrolled_in_linked_course"]},"SocialSpacesIdMembersItem":{"type":"object","properties":{"removed":{"type":"boolean","description":"Always true on success."}},"required":["removed"]},"SocialSpacesIdMembersPostItem":{"type":"object","properties":{"space_id":{"type":"string","format":"uuid","description":"The space."},"user_id":{"type":"string","format":"uuid","description":"The member added."},"role":{"type":"string","enum":["member","mod","owner"],"description":"Their role."}},"required":["space_id","user_id","role"]},"VersionsItem":{"type":"object","properties":{"version":{"type":"string","description":"Version identifier, e.g. \"v1\". This is the path segment."},"status":{"type":"string","enum":["current","deprecated","sunset"],"description":"Lifecycle state. Enforced by the API itself, not descriptive."},"released_on":{"type":"string","format":"date","description":"The day this version became available."},"deprecated_on":{"type":["string","null"],"format":"date","description":"The day deprecation began, or null while it is current."},"sunset_on":{"type":["string","null"],"format":"date","description":"The day it stops (or stopped) answering. Never null once deprecated."},"replacement_version":{"type":["string","null"],"description":"The version to move to. Never null once sunset."},"documentation_url":{"type":"string","description":"Where the migration for this version is documented."},"notes":{"type":"string","description":"What this version promises, and what may change within it without a new one."}},"required":["version","status","released_on","deprecated_on","sunset_on","replacement_version","documentation_url","notes"]}},"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","description":"Raw API key issued to the tenant, sent as `Authorization: Bearer <key>`. Hashed (sha256) server-side before lookup; the raw value is never stored or logged."}}}}